diff --git a/BouncyCastle.Crypto.dll b/BouncyCastle.Crypto.dll new file mode 100644 index 0000000..c73649f Binary files /dev/null and b/BouncyCastle.Crypto.dll differ diff --git a/MailKit.dll b/MailKit.dll new file mode 100644 index 0000000..19de400 Binary files /dev/null and b/MailKit.dll differ diff --git a/MimeKit.dll b/MimeKit.dll new file mode 100644 index 0000000..bd0148f Binary files /dev/null and b/MimeKit.dll differ diff --git a/prebuild.xml b/prebuild.xml index b4adb71..88bac3a 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -30,6 +30,10 @@ + + + + diff --git a/src/MailKit/AccessControl.cs b/src/MailKit/AccessControl.cs deleted file mode 100644 index cd7e5bb..0000000 --- a/src/MailKit/AccessControl.cs +++ /dev/null @@ -1,137 +0,0 @@ -// -// AccessControl.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -namespace MailKit { - /// - /// An Access Control. - /// - /// - /// An Access Control is a set of permissions available for a particular identity, - /// controlling whether or not that identity has the ability to perform various tasks. - /// - /// - /// - /// - public class AccessControl - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new with the given name and - /// access rights. - /// - /// The identifier name. - /// The access rights. - /// - /// is null. - /// -or- - /// is null. - /// - public AccessControl (string name, IEnumerable rights) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - Rights = new AccessRights (rights); - Name = name; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new with the given name and - /// access rights. - /// - /// The identifier name. - /// The access rights. - /// - /// is null. - /// -or- - /// is null. - /// - public AccessControl (string name, string rights) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - Rights = new AccessRights (rights); - Name = name; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new with the given name and no - /// access rights. - /// - /// The identifier name. - /// - /// is null. - /// - public AccessControl (string name) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - Rights = new AccessRights (); - Name = name; - } - - /// - /// The identifier name for the access control. - /// - /// - /// The identifier name for the access control. - /// - /// - /// - /// - /// The identifier name. - public string Name { - get; private set; - } - - /// - /// Get the access rights. - /// - /// - /// Gets the access rights. - /// - /// - /// - /// - /// The access rights. - public AccessRights Rights { - get; private set; - } - } -} diff --git a/src/MailKit/AccessControlList.cs b/src/MailKit/AccessControlList.cs deleted file mode 100644 index 06ae9c8..0000000 --- a/src/MailKit/AccessControlList.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -// AccessControlList.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Collections.Generic; - -namespace MailKit { - /// - /// An Access Control List (ACL) - /// - /// - /// An Access Control List (ACL) is a list of access controls defining the permissions - /// various identities have available. - /// - /// - /// - /// - public class AccessControlList : List - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The list of access controls. - /// - /// is null. - /// - public AccessControlList (IEnumerable controls) : base (controls) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public AccessControlList () - { - } - } -} diff --git a/src/MailKit/AccessRight.cs b/src/MailKit/AccessRight.cs deleted file mode 100644 index ccbb5db..0000000 --- a/src/MailKit/AccessRight.cs +++ /dev/null @@ -1,232 +0,0 @@ -// -// AccessRight.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// An individual Access Right to be used with ACLs. - /// - /// - /// An individual Access Right meant to be used with - /// . - /// For more information on what rights are available, - /// see https://tools.ietf.org/html/rfc4314#section-2.1 - /// - /// - public struct AccessRight : IEquatable - { - /// - /// The access right for folder lookups. - /// - /// - /// Allows the to be visible when listing folders. - /// - public static readonly AccessRight LookupFolder = new AccessRight ('l'); - - /// - /// The access right for opening a folder and getting the status. - /// - /// - /// Provides access for opening and getting the status of the folder. - /// - public static readonly AccessRight OpenFolder = new AccessRight ('r'); - - /// - /// The access right for adding or removing the Seen flag on messages in the folder. - /// - /// - /// Provides access to add or remove the flag on messages within the - /// . - /// - public static readonly AccessRight SetMessageSeen = new AccessRight ('s'); - - /// - /// The access right for adding or removing flags (other than Seen and Deleted) - /// on messages in a folder. - /// - /// - /// Provides access to add or remove the on messages - /// (other than and - /// ) within the folder. - /// - public static readonly AccessRight SetMessageFlags = new AccessRight ('w'); - - /// - /// The access right allowing messages to be appended or copied into the folder. - /// - /// - /// Provides access to append or copy messages into the folder. - /// - public static readonly AccessRight AppendMessages = new AccessRight ('i'); - - /// - /// The access right allowing subfolders to be created. - /// - /// - /// Provides access to create subfolders. - /// - public static readonly AccessRight CreateFolder = new AccessRight ('k'); - - /// - /// The access right for deleting a folder and/or its subfolders. - /// - /// - /// Provides access to delete the folder and/or any subfolders. - /// - public static readonly AccessRight DeleteFolder = new AccessRight ('x'); - - /// - /// The access right for adding or removing the Deleted flag to messages within a folder. - /// - /// - /// Provides access to add or remove the flag from - /// messages within the folder. It also provides access for setting the - /// flag when appending a message to a folder. - /// - public static readonly AccessRight SetMessageDeleted = new AccessRight ('t'); - - /// - /// The access right for expunging deleted messages in a folder. - /// - /// - /// Provides access to expunge deleted messages in a folder. - /// - public static readonly AccessRight ExpungeFolder = new AccessRight ('e'); - - /// - /// The access right for administering the ACLs of a folder. - /// - /// - /// Provides administrative access to change the ACLs for the folder. - /// - public static readonly AccessRight Administer = new AccessRight ('a'); - - /// - /// The character representing the particular access right. - /// - /// - /// Represents the character value of the access right. - /// - public readonly char Right; - - /// - /// Initializes a new instance of the struct. - /// - /// - /// Creates a new struct. - /// - /// The access right. - public AccessRight (char right) - { - Right = right; - } - - #region IEquatable implementation - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public bool Equals (AccessRight other) - { - return other.Right == Right; - } - - #endregion - - /// - /// Determines whether two access rights are equal. - /// - /// - /// Determines whether two access rights are equal. - /// - /// true if and are equal; otherwise, false. - /// The first access right to compare. - /// The second access right to compare. - public static bool operator == (AccessRight right1, AccessRight right2) - { - return right1.Right == right2.Right; - } - - /// - /// Determines whether two access rights are not equal. - /// - /// - /// Determines whether two access rights are not equal. - /// - /// true if and are not equal; otherwise, false. - /// The first access right to compare. - /// The second access right to compare. - public static bool operator != (AccessRight right1, AccessRight right2) - { - return right1.Right != right2.Right; - } - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current ; - /// otherwise, false. - public override bool Equals (object obj) - { - return obj is AccessRight && ((AccessRight) obj).Right == Right; - } - - /// - /// Serves as a hash function for a object. - /// - /// - /// Serves as a hash function for a object. - /// - /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a hash table. - public override int GetHashCode () - { - return Right.GetHashCode (); - } - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - return Right.ToString (); - } - } -} diff --git a/src/MailKit/AccessRights.cs b/src/MailKit/AccessRights.cs deleted file mode 100644 index 8a07a71..0000000 --- a/src/MailKit/AccessRights.cs +++ /dev/null @@ -1,317 +0,0 @@ -// -// AccessRights.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace MailKit { - /// - /// A set of access rights. - /// - /// - /// The set of access rights for a particular identity. - /// - public class AccessRights : ICollection - { - readonly List list = new List (); - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new set of access rights. - /// - /// The access rights. - /// - /// is null. - /// - public AccessRights (IEnumerable rights) - { - AddRange (rights); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new set of access rights. - /// - /// The access rights. - /// - /// is null. - /// - public AccessRights (string rights) - { - AddRange (rights); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates an empty set of access rights. - /// - public AccessRights () - { - } - - /// - /// Get the number of access rights in the collection. - /// - /// - /// Gets the number of access rights in the collection. - /// - /// The count. - public int Count { - get { return list.Count; } - } - - /// - /// Get whether or not this set of access rights is read only. - /// - /// - /// Gets whether or not this set of access rights is read only. - /// - /// true if this collection is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Add the specified access right. - /// - /// - /// Adds the specified access right if it is not already included. - /// - /// The access right. - void ICollection.Add (AccessRight right) - { - Add (right); - } - - /// - /// Add the specified access right. - /// - /// - /// Adds the specified access right if it is not already included. - /// - /// true if the right was added; otherwise, false. - /// The access right. - public bool Add (AccessRight right) - { - if (list.Contains (right)) - return false; - - list.Add (right); - - return true; - } - - /// - /// Add the specified right. - /// - /// - /// Adds the right specified by the given character. - /// - /// true if the right was added; otherwise, false. - /// The right. - public bool Add (char right) - { - return Add (new AccessRight (right)); - } - - /// - /// Add the rights specified by the characters in the given string. - /// - /// - /// Adds the rights specified by the characters in the given string. - /// - /// The rights. - /// - /// is null. - /// - public void AddRange (string rights) - { - if (rights == null) - throw new ArgumentNullException (nameof (rights)); - - for (int i = 0; i < rights.Length; i++) - Add (new AccessRight (rights[i])); - } - - /// - /// Add the range of specified rights. - /// - /// - /// Adds the range of specified rights. - /// - /// The rights. - /// - /// is null. - /// - public void AddRange (IEnumerable rights) - { - if (rights == null) - throw new ArgumentNullException (nameof (rights)); - - foreach (var right in rights) - Add (right); - } - - /// - /// Clears the access rights. - /// - /// - /// Removes all of the access rights. - /// - public void Clear () - { - list.Clear (); - } - - /// - /// Checks if the set of access rights contains the specified right. - /// - /// - /// Determines whether or not the set of access rights already contains the specified right - /// - /// true if the specified right exists; otherwise false. - /// The access right. - public bool Contains (AccessRight right) - { - return list.Contains (right); - } - - /// - /// Copies all of the access rights to the specified array. - /// - /// - /// Copies all of the access rights into the array, - /// starting at the specified array index. - /// - /// The array. - /// The array index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (AccessRight[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException (nameof (array)); - - if (arrayIndex < 0 || arrayIndex + Count > array.Length) - throw new ArgumentOutOfRangeException (nameof (arrayIndex)); - - list.CopyTo (array, arrayIndex); - } - - /// - /// Removes the specified access right. - /// - /// - /// Removes the specified access right. - /// - /// true if the access right was removed; otherwise false. - /// The access right. - public bool Remove (AccessRight right) - { - return list.Remove (right); - } - - /// - /// Get the access right at the specified index. - /// - /// - /// Gets the access right at the specified index. - /// - /// The access right at the specified index. - /// The index. - /// - /// is out of range. - /// - public AccessRight this [int index] { - get { - if (index < 0 || index >= list.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - return list[index]; - } - } - - #region IEnumerable implementation - - /// - /// Get the access rights enumerator. - /// - /// - /// Gets the access rights enumerator. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return list.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get the access rights enumerator. - /// - /// - /// Gets the access rights enumerator. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return list.GetEnumerator (); - } - - #endregion - - /// - /// Return a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - var rights = new char[list.Count]; - - for (int i = 0; i < list.Count; i++) - rights[i] = list[i].Right; - - return new string (rights); - } - } -} diff --git a/src/MailKit/AlertEventArgs.cs b/src/MailKit/AlertEventArgs.cs deleted file mode 100644 index 79c8279..0000000 --- a/src/MailKit/AlertEventArgs.cs +++ /dev/null @@ -1,69 +0,0 @@ -// -// AlertEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Alert event arguments. - /// - /// - /// Some implementations, such as - /// , will emit Alert - /// events when they receive alert messages from the server. - /// - public class AlertEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The alert message. - /// - /// is null. - /// - public AlertEventArgs (string message) - { - if (message == null) - throw new ArgumentNullException (nameof (message)); - - Message = message; - } - - /// - /// Gets the alert message. - /// - /// - /// The alert message will be the exact message received from the server. - /// - /// The alert message. - public string Message { - get; private set; - } - } -} diff --git a/src/MailKit/Annotation.cs b/src/MailKit/Annotation.cs deleted file mode 100644 index c62c61d..0000000 --- a/src/MailKit/Annotation.cs +++ /dev/null @@ -1,81 +0,0 @@ -// -// Annotation.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -namespace MailKit { - /// - /// An annotation. - /// - /// - /// An annotation. - /// For more information about annotations, see - /// rfc5257. - /// - public class Annotation - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The annotation entry. - /// - /// is null. - /// - public Annotation (AnnotationEntry entry) - { - if (entry == null) - throw new ArgumentNullException (nameof (entry)); - - Properties = new Dictionary (); - Entry = entry; - } - - /// - /// Get the annotation tag. - /// - /// - /// Gets the annotation tag. - /// - /// The annotation tag. - public AnnotationEntry Entry { - get; private set; - } - - /// - /// Get the annotation properties. - /// - /// - /// Gets the annotation properties. - /// - public Dictionary Properties { - get; private set; - } - } -} diff --git a/src/MailKit/AnnotationAccess.cs b/src/MailKit/AnnotationAccess.cs deleted file mode 100644 index b4e5f17..0000000 --- a/src/MailKit/AnnotationAccess.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// AnnotationAccess.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit { - /// - /// An annotation access level. - /// - /// - /// An annotation access level. - /// For more information about annotations, see - /// rfc5257. - /// - public enum AnnotationAccess - { - /// - /// Annotations are not supported. - /// - None, - - /// - /// Annotations are read-only. - /// - ReadOnly, - - /// - /// Annotations are read-write. - /// - ReadWrite - } -} diff --git a/src/MailKit/AnnotationAttribute.cs b/src/MailKit/AnnotationAttribute.cs deleted file mode 100644 index 06a4478..0000000 --- a/src/MailKit/AnnotationAttribute.cs +++ /dev/null @@ -1,251 +0,0 @@ -// -// AnnotationAttribute.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// An annotation attribute. - /// - /// - /// An annotation attribute. - /// For more information about annotations, see - /// rfc5257. - /// - public class AnnotationAttribute : IEquatable - { - static readonly char[] Wildcards = { '*', '%' }; - - /// - /// The annotation value. - /// - /// - /// Used to get or set both the private and shared values of an annotation. - /// - public static readonly AnnotationAttribute Value = new AnnotationAttribute ("value", AnnotationScope.Both); - - /// - /// The shared annotation value. - /// - /// - /// Used to get or set the shared value of an annotation. - /// - public static readonly AnnotationAttribute SharedValue = new AnnotationAttribute ("value", AnnotationScope.Shared); - - /// - /// The private annotation value. - /// - /// - /// Used to get or set the private value of an annotation. - /// - public static readonly AnnotationAttribute PrivateValue = new AnnotationAttribute ("value", AnnotationScope.Private); - - /// - /// The size of an annotation value. - /// - /// - /// Used to get the size of the both the private and shared annotation values. - /// - public static readonly AnnotationAttribute Size = new AnnotationAttribute ("size", AnnotationScope.Both); - - /// - /// The size of a shared annotation value. - /// - /// - /// Used to get the size of a shared annotation value. - /// - public static readonly AnnotationAttribute SharedSize = new AnnotationAttribute ("size", AnnotationScope.Shared); - - /// - /// The size of a private annotation value. - /// - /// - /// Used to get the size of a private annotation value. - /// - public static readonly AnnotationAttribute PrivateSize = new AnnotationAttribute ("size", AnnotationScope.Private); - - AnnotationAttribute (string name, AnnotationScope scope) - { - switch (scope) { - case AnnotationScope.Shared: Specifier = string.Format ("{0}.shared", name); break; - case AnnotationScope.Private: Specifier = string.Format ("{0}.priv", name); break; - default: Specifier = name; break; - } - Scope = scope; - Name = name; - } - - /// - /// Initializes a new instance of the class. - /// - /// The annotation attribute specifier. - /// - /// is null. - /// - /// - /// contains illegal characters. - /// - public AnnotationAttribute (string specifier) - { - if (specifier == null) - throw new ArgumentNullException (nameof (specifier)); - - if (specifier.Length == 0) - throw new ArgumentException ("Annotation attribute specifiers cannot be empty.", nameof (specifier)); - - // TODO: improve validation - if (specifier.IndexOfAny (Wildcards) != -1) - throw new ArgumentException ("Annotation attribute specifiers cannot contain '*' or '%'.", nameof (specifier)); - - Specifier = specifier; - - if (specifier.EndsWith (".shared", StringComparison.Ordinal)) { - Name = specifier.Substring (0, specifier.Length - ".shared".Length); - Scope = AnnotationScope.Shared; - } else if (specifier.EndsWith (".priv", StringComparison.Ordinal)) { - Name = specifier.Substring (0, specifier.Length - ".priv".Length); - Scope = AnnotationScope.Private; - } else { - Scope = AnnotationScope.Both; - Name = specifier; - } - } - - /// - /// Get the name of the annotation attribute. - /// - /// - /// Gets the name of the annotation attribute. - /// - public string Name { - get; private set; - } - - /// - /// Get the scope of the annotation attribute. - /// - /// - /// Gets the scope of the annotation attribute. - /// - public AnnotationScope Scope { - get; private set; - } - - /// - /// Get the annotation attribute specifier. - /// - /// - /// Gets the annotation attribute specifier. - /// - public string Specifier { - get; private set; - } - - #region IEquatable implementation - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public bool Equals (AnnotationAttribute other) - { - return other?.Specifier == Specifier; - } - - #endregion - - /// - /// Determines whether two annotation attributes are equal. - /// - /// - /// Determines whether two annotation attributes are equal. - /// - /// true if and are equal; otherwise, false. - /// The first annotation attribute to compare. - /// The second annotation attribute to compare. - public static bool operator == (AnnotationAttribute attr1, AnnotationAttribute attr2) - { - return attr1?.Specifier == attr2?.Specifier; - } - - /// - /// Determines whether two annotation attributes are not equal. - /// - /// - /// Determines whether two annotation attributes are not equal. - /// - /// true if and are not equal; otherwise, false. - /// The first annotation attribute to compare. - /// The second annotation attribute to compare. - public static bool operator != (AnnotationAttribute attr1, AnnotationAttribute attr2) - { - return attr1?.Specifier != attr2?.Specifier; - } - - /// - /// Determine whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public override bool Equals (object obj) - { - return obj is AnnotationAttribute && ((AnnotationAttribute) obj).Specifier == Specifier; - } - - /// - /// Serves as a hash function for a object. - /// - /// - /// Serves as a hash function for a object. - /// - /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a hash table. - public override int GetHashCode () - { - return Specifier.GetHashCode (); - } - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - return Specifier; - } - } -} diff --git a/src/MailKit/AnnotationEntry.cs b/src/MailKit/AnnotationEntry.cs deleted file mode 100644 index 8f510e1..0000000 --- a/src/MailKit/AnnotationEntry.cs +++ /dev/null @@ -1,521 +0,0 @@ -// -// Annotationentry.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// An annotation entry. - /// - /// - /// An annotation entry. - /// For more information about annotations, see - /// rfc5257. - /// - public class AnnotationEntry : IEquatable - { - /// - /// An annotation entry for a comment on a message. - /// - /// - /// Used to get or set a comment on a message. - /// - public static readonly AnnotationEntry Comment = new AnnotationEntry ("/comment", AnnotationScope.Both); - - /// - /// An annotation entry for a private comment on a message. - /// - /// - /// Used to get or set a private comment on a message. - /// - public static readonly AnnotationEntry PrivateComment = new AnnotationEntry ("/comment", AnnotationScope.Private); - - /// - /// An annotation entry for a shared comment on a message. - /// - /// - /// Used to get or set a shared comment on a message. - /// - public static readonly AnnotationEntry SharedComment = new AnnotationEntry ("/comment", AnnotationScope.Shared); - - /// - /// An annotation entry for flags on a message. - /// - /// - /// Used to get or set flags on a message. - /// - public static readonly AnnotationEntry Flags = new AnnotationEntry ("/flags", AnnotationScope.Both); - - /// - /// An annotation entry for private flags on a message. - /// - /// - /// Used to get or set private flags on a message. - /// - public static readonly AnnotationEntry PrivateFlags = new AnnotationEntry ("/flags", AnnotationScope.Private); - - /// - /// Aa annotation entry for shared flags on a message. - /// - /// - /// Used to get or set shared flags on a message. - /// - public static readonly AnnotationEntry SharedFlags = new AnnotationEntry ("/flags", AnnotationScope.Shared); - - /// - /// An annotation entry for an alternate subject on a message. - /// - /// - /// Used to get or set an alternate subject on a message. - /// - public static readonly AnnotationEntry AltSubject = new AnnotationEntry ("/altsubject", AnnotationScope.Both); - - /// - /// An annotation entry for a private alternate subject on a message. - /// - /// - /// Used to get or set a private alternate subject on a message. - /// - public static readonly AnnotationEntry PrivateAltSubject = new AnnotationEntry ("/altsubject", AnnotationScope.Private); - - /// - /// An annotation entry for a shared alternate subject on a message. - /// - /// - /// Used to get or set a shared alternate subject on a message. - /// - public static readonly AnnotationEntry SharedAltSubject = new AnnotationEntry ("/altsubject", AnnotationScope.Shared); - - static void ValidatePath (string path) - { - if (path == null) - throw new ArgumentNullException (nameof (path)); - - if (path.Length == 0) - throw new ArgumentException ("Annotation entry paths cannot be empty.", nameof (path)); - - if (path[0] != '/' && path[0] != '*' && path[0] != '%') - throw new ArgumentException ("Annotation entry paths must begin with '/'.", nameof (path)); - - if (path.Length > 1 && path[1] >= '0' && path[1] <= '9') - throw new ArgumentException ("Annotation entry paths must not include a part-specifier.", nameof (path)); - - if (path == "*" || path == "%") - return; - - char pc = path[0]; - - for (int i = 1; i < path.Length; i++) { - char c = path[i]; - - if (c > 127) - throw new ArgumentException ($"Invalid character in annotation entry path: '{c}'.", nameof (path)); - - if (c >= '0' && c <= '9' && pc == '/') - throw new ArgumentException ("Invalid annotation entry path.", nameof (path)); - - if ((pc == '/' || pc == '.') && (c == '/' || c == '.')) - throw new ArgumentException ("Invalid annotation entry path.", nameof (path)); - - pc = c; - } - - int endIndex = path.Length - 1; - - if (path[endIndex] == '/') - throw new ArgumentException ("Annotation entry paths must not end with '/'.", nameof (path)); - - if (path[endIndex] == '.') - throw new ArgumentException ("Annotation entry paths must not end with '.'.", nameof (path)); - } - - static void ValidatePartSpecifier (string partSpecifier) - { - if (partSpecifier == null) - throw new ArgumentNullException (nameof (partSpecifier)); - - char pc = '\0'; - - for (int i = 0; i < partSpecifier.Length; i++) { - char c = partSpecifier[i]; - - if (!((c >= '0' && c <= '9') || c == '.') || (c == '.' && (pc == '.' || pc == '\0'))) - throw new ArgumentException ("Invalid part-specifier.", nameof (partSpecifier)); - - pc = c; - } - - if (pc == '.') - throw new ArgumentException ("Invalid part-specifier.", nameof (partSpecifier)); - } - - AnnotationEntry () - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// Creates a new . - /// - /// The annotation entry path. - /// The scope of the annotation. - /// - /// is null. - /// - /// - /// is invalid. - /// - public AnnotationEntry (string path, AnnotationScope scope = AnnotationScope.Both) - { - ValidatePath (path); - - switch (scope) { - case AnnotationScope.Private: Entry = path + ".priv"; break; - case AnnotationScope.Shared: Entry = path + ".shared"; break; - default: Entry = path; break; - } - PartSpecifier = null; - Path = path; - Scope = scope; - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// Creates a new for an individual body part of a message. - /// - /// The part-specifier of the body part of the message. - /// The annotation entry path. - /// The scope of the annotation. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// -or- - /// is invalid. - /// - public AnnotationEntry (string partSpecifier, string path, AnnotationScope scope = AnnotationScope.Both) - { - ValidatePartSpecifier (partSpecifier); - ValidatePath (path); - - switch (scope) { - case AnnotationScope.Private: Entry = string.Format ("/{0}{1}.priv", partSpecifier, path); break; - 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; - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// Creates a new for an individual body part of a message. - /// - /// The body part of the message. - /// The annotation entry path. - /// The scope of the annotation. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - public AnnotationEntry (BodyPart part, string path, AnnotationScope scope = AnnotationScope.Both) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - ValidatePath (path); - - switch (scope) { - case AnnotationScope.Private: Entry = string.Format ("/{0}{1}.priv", part.PartSpecifier, path); break; - 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; - } - - /// - /// Get the annotation entry specifier. - /// - /// - /// Gets the annotation entry specifier. - /// - /// The annotation entry specifier. - public string Entry { - get; private set; - } - - /// - /// Get the part-specifier component of the annotation entry. - /// - /// - /// Gets the part-specifier component of the annotation entry. - /// - public string PartSpecifier { - get; private set; - } - - /// - /// Get the path component of the annotation entry. - /// - /// - /// Gets the path component of the annotation entry. - /// - public string Path { - get; private set; - } - - /// - /// Get the scope of the annotation. - /// - /// - /// Gets the scope of the annotation. - /// - public AnnotationScope Scope { - get; private set; - } - - #region IEquatable implementation - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public bool Equals (AnnotationEntry other) - { - return other?.Entry == Entry; - } - - #endregion - - /// - /// Determines whether two annotation entries are equal. - /// - /// - /// Determines whether two annotation entries are equal. - /// - /// true if and are equal; otherwise, false. - /// The first annotation entry to compare. - /// The second annotation entry to compare. - public static bool operator == (AnnotationEntry entry1, AnnotationEntry entry2) - { - return entry1?.Entry == entry2?.Entry; - } - - /// - /// Determines whether two annotation entries are not equal. - /// - /// - /// Determines whether two annotation entries are not equal. - /// - /// true if and are not equal; otherwise, false. - /// The first annotation entry to compare. - /// The second annotation entry to compare. - public static bool operator != (AnnotationEntry entry1, AnnotationEntry entry2) - { - return entry1?.Entry != entry2?.Entry; - } - - /// - /// Determine whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public override bool Equals (object obj) - { - return obj is AnnotationEntry && ((AnnotationEntry) obj).Entry == Entry; - } - - /// - /// Serves as a hash function for a object. - /// - /// - /// Serves as a hash function for a object. - /// - /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a hash table. - public override int GetHashCode () - { - return Entry.GetHashCode (); - } - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - return Entry; - } - - /// - /// Parse an annotation entry. - /// - /// - /// Parses an annotation entry. - /// - /// The annotation entry. - /// The parsed annotation entry. - /// - /// is null. - /// - /// - /// does not conform to the annotation entry syntax. - /// - public static AnnotationEntry Parse (string entry) - { - if (entry == null) - throw new ArgumentNullException (nameof (entry)); - - if (entry.Length == 0) - throw new FormatException ("An annotation entry cannot be empty."); - - if (entry[0] != '/' && entry[0] != '*' && entry[0] != '%') - throw new FormatException ("An annotation entry must begin with a '/' character."); - - var scope = AnnotationScope.Both; - int startIndex = 0, endIndex; - string partSpecifier = null; - var component = 0; - var pc = entry[0]; - string path; - - for (int i = 1; i < entry.Length; i++) { - char c = entry[i]; - - if (c >= '0' && c <= '9' && pc == '/') { - if (component > 0) - throw new FormatException ("Invalid annotation entry."); - - startIndex = i; - endIndex = i + 1; - pc = c; - - while (endIndex < entry.Length) { - c = entry[endIndex]; - - if (c == '/') { - if (pc == '.') - throw new FormatException ("Invalid part-specifier in annotation entry."); - - break; - } - - if (!(c >= '0' && c <= '9') && c != '.') - throw new FormatException ($"Invalid character in part-specifier: '{c}'."); - - if (c == '.' && pc == '.') - throw new FormatException ("Invalid part-specifier in annotation entry."); - - endIndex++; - pc = c; - } - - if (endIndex >= entry.Length) - throw new FormatException ("Incomplete part-specifier in annotation entry."); - - partSpecifier = entry.Substring (startIndex, endIndex - startIndex); - i = startIndex = endIndex; - component++; - } else if (c == '/' || c == '.') { - if (pc == '/' || pc == '.') - throw new FormatException ("Invalid annotation entry path."); - - if (c == '/') - component++; - } else if (c > 127) { - throw new FormatException ($"Invalid character in annotation entry path: '{c}'."); - } - - pc = c; - } - - if (pc == '/' || pc == '.') - throw new FormatException ("Invalid annotation entry path."); - - if (entry.EndsWith (".shared", StringComparison.Ordinal)) { - endIndex = entry.Length - ".shared".Length; - scope = AnnotationScope.Shared; - } else if (entry.EndsWith (".priv", StringComparison.Ordinal)) { - endIndex = entry.Length - ".priv".Length; - scope = AnnotationScope.Private; - } else { - endIndex = entry.Length; - } - - path = entry.Substring (startIndex, endIndex - startIndex); - - return new AnnotationEntry { - PartSpecifier = partSpecifier, - Entry = entry, - Path = path, - Scope = scope - }; - } - - internal static AnnotationEntry Create (string entry) - { - switch (entry) { - case "/comment": return Comment; - case "/comment.priv": return PrivateComment; - case "/comment.shared": return SharedComment; - case "/flags": return Flags; - case "/flags.priv": return PrivateFlags; - case "/flags.shared": return SharedFlags; - case "/altsubject": return AltSubject; - case "/altsubject.priv": return PrivateAltSubject; - case "/altsubject.shared": return SharedAltSubject; - default: return Parse (entry); - } - } - } -} diff --git a/src/MailKit/AnnotationScope.cs b/src/MailKit/AnnotationScope.cs deleted file mode 100644 index c091a70..0000000 --- a/src/MailKit/AnnotationScope.cs +++ /dev/null @@ -1,61 +0,0 @@ -// -// AnnotationScope.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// The scope of an annotation. - /// - /// - /// Represents the scope of an annotation. - /// For more information about annotations, see - /// rfc5257. - /// - [Flags] - public enum AnnotationScope - { - /// - /// No scopes. - /// - None, - - /// - /// The private annotation scope. - /// - Private, - - /// - /// The shared annotation scope. - /// - Shared, - - /// - /// Both private and shared scopes. - /// - Both = Private | Shared - } -} diff --git a/src/MailKit/AnnotationsChangedEventArgs.cs b/src/MailKit/AnnotationsChangedEventArgs.cs deleted file mode 100644 index b3d4015..0000000 --- a/src/MailKit/AnnotationsChangedEventArgs.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// AnnotationsChangedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace MailKit { - /// - /// Event args used when an annotation changes. - /// - /// - /// Event args used when an annotation changes. - /// - 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. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The annotations that changed. - /// - /// is null. - /// - public AnnotationsChangedEventArgs (int index, IEnumerable annotations) : base (index) - { - if (annotations == null) - throw new ArgumentNullException (nameof (annotations)); - - Annotations = new ReadOnlyCollection (annotations.ToArray ()); - } - - /// - /// Get the annotations that changed. - /// - /// - /// Gets the annotations that changed. - /// - /// The annotation. - public IList Annotations { - get; internal set; - } - - /// - /// Gets the updated mod-sequence value of the message, if available. - /// - /// - /// Gets the updated mod-sequence value of the message, if available. - /// - /// The mod-sequence value. - public ulong? ModSeq { - get; internal set; - } - } -} diff --git a/src/MailKit/AuthenticatedEventArgs.cs b/src/MailKit/AuthenticatedEventArgs.cs deleted file mode 100644 index 5f9467a..0000000 --- a/src/MailKit/AuthenticatedEventArgs.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// AuthenticatedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Authenticated event arguments. - /// - /// - /// Some servers, such as GMail IMAP, will send some free-form text in - /// the response to a successful login. - /// - public class AuthenticatedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The free-form text. - /// - /// is null. - /// - public AuthenticatedEventArgs (string message) - { - if (message == null) - throw new ArgumentNullException (nameof (message)); - - Message = message; - } - - /// - /// Get the free-form text sent by the server. - /// - /// - /// Gets the free-form text sent by the server. - /// - /// The free-form text sent by the server. - public string Message { - get; private set; - } - } -} diff --git a/src/MailKit/BodyPart.cs b/src/MailKit/BodyPart.cs deleted file mode 100644 index 3d3a548..0000000 --- a/src/MailKit/BodyPart.cs +++ /dev/null @@ -1,716 +0,0 @@ -// -// BodyPart.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections.Generic; - -using MimeKit; -using MimeKit.Utils; - -namespace MailKit { - /// - /// An abstract body part of a message. - /// - /// - /// Each body part will actually be a , - /// , , or - /// . - /// - /// - /// - /// - public abstract class BodyPart - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - protected BodyPart () - { - } - - /// - /// Gets the Content-Type of the body part. - /// - /// - /// Gets the Content-Type of the body part. - /// - /// The content type. - public ContentType ContentType { - get; set; - } - - /// - /// Gets the part specifier. - /// - /// - /// Gets the part specifier. - /// - /// - /// - /// - /// The part specifier. - public string PartSpecifier { - get; set; - } - - /// - /// Dispatches to the specific visit method for this MIME body part. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public abstract void Accept (BodyPartVisitor visitor); - - internal static void Encode (StringBuilder builder, uint value) - { - builder.Append (value.ToString ()); - } - - internal static void Encode (StringBuilder builder, string value) - { - if (value != null) - builder.Append (MimeUtils.Quote (value)); - else - builder.Append ("NIL"); - } - - internal static void Encode (StringBuilder builder, Uri location) - { - if (location != null) - builder.Append (MimeUtils.Quote (location.ToString ())); - else - builder.Append ("NIL"); - } - - internal static void Encode (StringBuilder builder, string[] values) - { - if (values == null || values.Length == 0) { - builder.Append ("NIL"); - return; - } - - builder.Append ('('); - - for (int i = 0; i < values.Length; i++) { - if (i > 0) - builder.Append (' '); - - Encode (builder, values[i]); - } - - builder.Append (')'); - } - - internal static void Encode (StringBuilder builder, IList parameters) - { - if (parameters == null || parameters.Count == 0) { - builder.Append ("NIL"); - return; - } - - builder.Append ('('); - - for (int i = 0; i < parameters.Count; i++) { - if (i > 0) - builder.Append (' '); - - Encode (builder, parameters[i].Name); - builder.Append (' '); - Encode (builder, parameters[i].Value); - } - - builder.Append (')'); - } - - internal static void Encode (StringBuilder builder, ContentDisposition disposition) - { - if (disposition == null) { - builder.Append ("NIL"); - return; - } - - builder.Append ('('); - Encode (builder, disposition.Disposition); - builder.Append (' '); - Encode (builder, disposition.Parameters); - builder.Append (')'); - } - - internal static void Encode (StringBuilder builder, ContentType contentType) - { - Encode (builder, contentType.MediaType); - builder.Append (' '); - Encode (builder, contentType.MediaSubtype); - builder.Append (' '); - Encode (builder, contentType.Parameters); - } - - internal static void Encode (StringBuilder builder, BodyPartCollection parts) - { - if (parts == null || parts.Count == 0) { - builder.Append ("NIL"); - return; - } - - for (int i = 0; i < parts.Count; i++) { - if (i > 0) - builder.Append (' '); - - Encode (builder, parts[i]); - } - } - - internal static void Encode (StringBuilder builder, Envelope envelope) - { - if (envelope == null) { - builder.Append ("NIL"); - return; - } - - envelope.Encode (builder); - } - - internal static void Encode (StringBuilder builder, BodyPart body) - { - if (body == null) { - builder.Append ("NIL"); - return; - } - - builder.Append ('('); - body.Encode (builder); - builder.Append (')'); - } - - /// - /// Encodes the into the . - /// - /// - /// Encodes the into the . - /// - /// The string builder. - protected abstract void Encode (StringBuilder builder); - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// The syntax of the string returned, while similar to IMAP's BODYSTRUCTURE syntax, - /// is not completely compatible. - /// - /// A that represents the current . - public override string ToString () - { - var builder = new StringBuilder (); - - builder.Append ('('); - Encode (builder); - builder.Append (')'); - - return builder.ToString (); - } - - static bool TryParse (string text, ref int index, out uint value) - { - while (index < text.Length && text[index] == ' ') - index++; - - int startIndex = index; - - value = 0; - - while (index < text.Length && char.IsDigit (text[index])) - value = (value * 10) + (uint) (text[index++] - '0'); - - return index > startIndex; - } - - static bool TryParse (string text, ref int index, out string nstring) - { - nstring = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length) - return false; - - if (text[index] != '"') { - if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") { - index += 3; - return true; - } - - return false; - } - - var token = new StringBuilder (); - bool escaped = false; - - index++; - - while (index < text.Length) { - if (text[index] == '"' && !escaped) - break; - - if (escaped || text[index] != '\\') { - token.Append (text[index]); - escaped = false; - } else { - escaped = true; - } - - index++; - } - - if (index >= text.Length) - return false; - - nstring = token.ToString (); - - index++; - - return true; - } - - static bool TryParse (string text, ref int index, out string[] values) - { - values = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length) - return false; - - if (text[index] != '(') { - if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") { - index += 3; - return true; - } - - return false; - } - - index++; - - if (index >= text.Length) - return false; - - var list = new List (); - string value; - - do { - if (text[index] == ')') - break; - - if (!TryParse (text, ref index, out value)) - return false; - - list.Add (value); - } while (index < text.Length); - - if (index >= text.Length || text[index] != ')') - return false; - - values = list.ToArray (); - index++; - - return true; - } - - static bool TryParse (string text, ref int index, out Uri uri) - { - string nstring; - - uri = null; - - if (!TryParse (text, ref index, out nstring)) - return false; - - if (!string.IsNullOrEmpty (nstring)) { - if (Uri.IsWellFormedUriString (nstring, UriKind.Absolute)) - uri = new Uri (nstring, UriKind.Absolute); - else if (Uri.IsWellFormedUriString (nstring, UriKind.Relative)) - uri = new Uri (nstring, UriKind.Relative); - } - - return true; - } - - static bool TryParse (string text, ref int index, out IList parameters) - { - string name, value; - - parameters = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length) - return false; - - if (text[index] != '(') { - if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") { - parameters = new List (); - index += 3; - return true; - } - - return false; - } - - index++; - - if (index >= text.Length) - return false; - - parameters = new List (); - - do { - if (text[index] == ')') - break; - - if (!TryParse (text, ref index, out name)) - return false; - - if (!TryParse (text, ref index, out value)) - return false; - - parameters.Add (new Parameter (name, value)); - } while (index < text.Length); - - if (index >= text.Length || text[index] != ')') - return false; - - index++; - - return true; - } - - static bool TryParse (string text, ref int index, out ContentDisposition disposition) - { - IList parameters; - string value; - - disposition = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length) - return false; - - if (text[index] != '(') { - if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") { - index += 3; - return true; - } - - return false; - } - - index++; - - if (!TryParse (text, ref index, out value)) - return false; - - if (!TryParse (text, ref index, out parameters)) - return false; - - if (index >= text.Length || text[index] != ')') - return false; - - index++; - - disposition = new ContentDisposition (value); - - foreach (var param in parameters) - disposition.Parameters.Add (param); - - return true; - } - - static bool TryParse (string text, ref int index, bool multipart, out ContentType contentType) - { - IList parameters; - string type, subtype; - - contentType = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length) - return false; - - if (!multipart) { - if (!TryParse (text, ref index, out type)) - return false; - } else { - type = "multipart"; - } - - if (!TryParse (text, ref index, out subtype)) - return false; - - if (!TryParse (text, ref index, out parameters)) - return false; - - contentType = new ContentType (type ?? "application", subtype ?? "octet-stream"); - - foreach (var param in parameters) - contentType.Parameters.Add (param); - - return true; - } - - static bool TryParse (string text, ref int index, string prefix, out IList children) - { - BodyPart part; - string path; - int id = 1; - - children = null; - - if (index >= text.Length) - return false; - - children = new List (); - - do { - if (text[index] != '(') - break; - - path = prefix + id; - - if (!TryParse (text, ref index, path, out part)) - return false; - - while (index < text.Length && text[index] == ' ') - index++; - - children.Add (part); - id++; - } while (index < text.Length); - - return index < text.Length; - } - - static bool TryParse (string text, ref int index, string path, out BodyPart part) - { - ContentDisposition disposition; - ContentType contentType; - string[] array; - string nstring; - Uri location; - uint number; - - part = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length || text[index] != '(') { - if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") { - index += 3; - return true; - } - - return false; - } - - index++; - - if (index >= text.Length) - return false; - - if (text[index] == '(') { - var prefix = path.Length > 0 ? path + "." : string.Empty; - var multipart = new BodyPartMultipart (); - IList children; - - if (!TryParse (text, ref index, prefix, out children)) - return false; - - foreach (var child in children) - multipart.BodyParts.Add (child); - - if (!TryParse (text, ref index, true, out contentType)) - return false; - - multipart.ContentType = contentType; - - if (!TryParse (text, ref index, out disposition)) - return false; - - multipart.ContentDisposition = disposition; - - if (!TryParse (text, ref index, out array)) - return false; - - multipart.ContentLanguage = array; - - if (!TryParse (text, ref index, out location)) - return false; - - multipart.ContentLocation = location; - - part = multipart; - } else { - BodyPartMessage message = null; - BodyPartText txt = null; - BodyPartBasic basic; - - if (!TryParse (text, ref index, false, out contentType)) - return false; - - if (contentType.IsMimeType ("message", "rfc822")) - basic = message = new BodyPartMessage (); - else if (contentType.IsMimeType ("text", "*")) - basic = txt = new BodyPartText (); - else - basic = new BodyPartBasic (); - - basic.ContentType = contentType; - - if (!TryParse (text, ref index, out nstring)) - return false; - - basic.ContentId = nstring; - - if (!TryParse (text, ref index, out nstring)) - return false; - - basic.ContentDescription = nstring; - - if (!TryParse (text, ref index, out nstring)) - return false; - - basic.ContentTransferEncoding = nstring; - - if (!TryParse (text, ref index, out number)) - return false; - - basic.Octets = number; - - if (!TryParse (text, ref index, out nstring)) - return false; - - basic.ContentMd5 = nstring; - - if (!TryParse (text, ref index, out disposition)) - return false; - - basic.ContentDisposition = disposition; - - if (!TryParse (text, ref index, out array)) - return false; - - basic.ContentLanguage = array; - - if (!TryParse (text, ref index, out location)) - return false; - - basic.ContentLocation = location; - - if (message != null) { - Envelope envelope; - BodyPart body; - - if (!Envelope.TryParse (text, ref index, out envelope)) - return false; - - message.Envelope = envelope; - - if (!TryParse (text, ref index, path, out body)) - return false; - - message.Body = body; - - if (!TryParse (text, ref index, out number)) - return false; - - message.Lines = number; - } else if (txt != null) { - if (!TryParse (text, ref index, out number)) - return false; - - txt.Lines = number; - } - - part = basic; - } - - part.PartSpecifier = path; - - if (index >= text.Length || text[index] != ')') - return false; - - index++; - - return true; - } - - /// - /// Tries to parse the given text into a new instance. - /// - /// - /// Parses a body part from the specified text. - /// This syntax, while similar to IMAP's BODYSTRUCTURE syntax, is not completely - /// compatible. - /// - /// true, if the body part was successfully parsed, false otherwise. - /// The text to parse. - /// The parsed body part. - /// - /// is null. - /// - public static bool TryParse (string text, out BodyPart part) - { - if (text == null) - throw new ArgumentNullException (nameof (text)); - - int index = 0; - - return TryParse (text, ref index, string.Empty, out part) && index == text.Length; - } - } -} diff --git a/src/MailKit/BodyPartBasic.cs b/src/MailKit/BodyPartBasic.cs deleted file mode 100644 index 17ffdf6..0000000 --- a/src/MailKit/BodyPartBasic.cs +++ /dev/null @@ -1,245 +0,0 @@ -// -// BodyPartBasic.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -using MimeKit; - -namespace MailKit { - /// - /// A basic message body part. - /// - /// - /// Represents any message body part that is not a multipart, - /// message/rfc822 part, or a text part. - /// - /// - /// - /// - public class BodyPartBasic : BodyPart - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public BodyPartBasic () - { - } - - /// - /// Gets the Content-Id of the body part, if available. - /// - /// - /// Gets the Content-Id of the body part, if available. - /// - /// The content identifier. - public string ContentId { - get; set; - } - - /// - /// Gets the Content-Description of the body part, if available. - /// - /// - /// Gets the Content-Description of the body part, if available. - /// - /// The content description. - public string ContentDescription { - get; set; - } - - /// - /// Gets the Content-Transfer-Encoding of the body part. - /// - /// - /// Gets the Content-Transfer-Encoding of the body part. - /// Hint: Use the MimeUtils.TryParse - /// method to parse this value into a usable . - /// - /// The content transfer encoding. - public string ContentTransferEncoding { - get; set; - } - - /// - /// Gets the size of the body part, in bytes. - /// - /// - /// Gets the size of the body part, in bytes. - /// - /// The number of octets. - public uint Octets { - get; set; - } - - /// - /// Gets the MD5 hash of the content, if available. - /// - /// - /// Gets the MD5 hash of the content, if available. - /// - /// The content md5. - public string ContentMd5 { - get; set; - } - - /// - /// Gets the Content-Disposition of the body part, if available. - /// - /// - /// Gets the Content-Disposition of the body part, if available. - /// The Content-Disposition value is only retrieved if the - /// flag is used when fetching - /// summary information from an . - /// - /// The content disposition. - public ContentDisposition ContentDisposition { - get; set; - } - - /// - /// Gets the Content-Language of the body part, if available. - /// - /// - /// Gets the Content-Language of the body part, if available. - /// The Content-Language value is only retrieved if the - /// flag is used when fetching - /// summary information from an . - /// - /// The content language. - public string[] ContentLanguage { - get; set; - } - - /// - /// Gets the Content-Location of the body part, if available. - /// - /// - /// Gets the Content-Location of the body part, if available. - /// The Content-Location value is only retrieved if the - /// flag is used when fetching - /// summary information from an . - /// - /// The content location. - public Uri ContentLocation { - get; set; - } - - /// - /// Determines whether or not the body part is an attachment. - /// - /// - /// Determines whether or not the body part is an attachment based on the value of - /// the Content-Disposition. - /// Since the value of the Content-Disposition header is needed, it - /// is necessary to include the flag when - /// fetching summary information from an . - /// - /// true if this part is an attachment; otherwise, false. - public bool IsAttachment { - get { return ContentDisposition != null && ContentDisposition.IsAttachment; } - } - - /// - /// Get the name of the file. - /// - /// - /// First checks for the "filename" parameter on the Content-Disposition header. If - /// that does not exist, then the "name" parameter on the Content-Type header is used. - /// Since the value of the Content-Disposition header is needed, it is - /// necessary to include the flag when - /// fetching summary information from an . - /// - /// The name of the file. - public string FileName { - get { - string filename = null; - - if (ContentDisposition != null) - filename = ContentDisposition.FileName; - - if (filename == null) - filename = ContentType.Name; - - return filename != null ? filename.Trim () : null; - } - } - - /// - /// Dispatches to the specific visit method for this MIME body part. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (BodyPartVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitBodyPartBasic (this); - } - - /// - /// Encodes the into the . - /// - /// - /// Encodes the into the . - /// - /// The string builder. - protected override void Encode (StringBuilder builder) - { - Encode (builder, ContentType); - builder.Append (' '); - Encode (builder, ContentId); - builder.Append (' '); - Encode (builder, ContentDescription); - builder.Append (' '); - Encode (builder, ContentTransferEncoding); - builder.Append (' '); - Encode (builder, Octets); - builder.Append (' '); - Encode (builder, ContentMd5); - builder.Append (' '); - Encode (builder, ContentDisposition); - builder.Append (' '); - Encode (builder, ContentLanguage); - builder.Append (' '); - Encode (builder, ContentLocation); - } - } -} diff --git a/src/MailKit/BodyPartCollection.cs b/src/MailKit/BodyPartCollection.cs deleted file mode 100644 index bbd78bb..0000000 --- a/src/MailKit/BodyPartCollection.cs +++ /dev/null @@ -1,276 +0,0 @@ -// -// BodyPartCollection.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Collections; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MailKit { - /// - /// A collection. - /// - /// - /// A collection. - /// - public class BodyPartCollection : ICollection - { - readonly List collection = new List (); - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public BodyPartCollection () - { - } - - /// - /// Get the number of body parts in the collection. - /// - /// - /// Gets the number of body parts in the collection. - /// - /// The count. - public int Count { - get { return collection.Count; } - } - - /// - /// Get whether or not this body part collection is read only. - /// - /// - /// Gets whether or not this body part collection is read only. - /// - /// true if this collection is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Add the specified body part to the collection. - /// - /// - /// Adds the specified body part to the collection. - /// - /// The body part. - /// - /// is null. - /// - public void Add (BodyPart part) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - collection.Add (part); - } - - /// - /// Clears the body part collection. - /// - /// - /// Removes all of the body parts from the collection. - /// - public void Clear () - { - collection.Clear (); - } - - /// - /// Checks if the collection contains the specified body part. - /// - /// - /// Determines whether or not the collection contains the specified body part. - /// - /// true if the specified body part exists; otherwise false. - /// The body part. - /// - /// is null. - /// - public bool Contains (BodyPart part) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return collection.Contains (part); - } - - /// - /// Copies all of the body parts in the collection to the specified array. - /// - /// - /// Copies all of the body parts within the collection into the array, - /// starting at the specified array index. - /// - /// The array. - /// The array index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (BodyPart[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException (nameof (array)); - - if (arrayIndex < 0 || arrayIndex + Count > array.Length) - throw new ArgumentOutOfRangeException (nameof (arrayIndex)); - - collection.CopyTo (array, arrayIndex); - } - - /// - /// Removes the specified body part. - /// - /// - /// Removes the specified body part. - /// - /// true if the body part was removed; otherwise false. - /// The body part. - /// - /// is null. - /// - public bool Remove (BodyPart part) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return collection.Remove (part); - } - - /// - /// Get the body part at the specified index. - /// - /// - /// Gets the body part at the specified index. - /// - /// The body part at the specified index. - /// The index. - /// - /// is out of range. - /// - public BodyPart this [int index] { - get { - if (index < 0 || index >= collection.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - return collection[index]; - } - } - - /// - /// Gets the index of the body part matching the specified URI. - /// - /// - /// Finds the index of the body part matching the specified URI, if it exists. - /// If the URI scheme is "cid", then matching is performed based on the Content-Id header - /// values, otherwise the Content-Location headers are used. If the provided URI is absolute and a child - /// part's Content-Location is relative, then then the child part's Content-Location URI will be combined - /// with the value of its Content-Base header, if available, otherwise it will be combined with the - /// multipart/related part's Content-Base header in order to produce an absolute URI that can be - /// compared with the provided absolute URI. - /// - /// The index of the part matching the specified URI if found; otherwise -1. - /// The URI of the body part. - /// - /// is null. - /// - public int IndexOf (Uri uri) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - bool cid = uri.IsAbsoluteUri && uri.Scheme.ToLowerInvariant () == "cid"; - - for (int index = 0; index < Count; index++) { - var bodyPart = this[index] as BodyPartBasic; - - if (bodyPart == null) - continue; - - if (uri.IsAbsoluteUri) { - 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; - - if (id == uri.AbsolutePath) - return index; - } - } else if (bodyPart.ContentLocation != null) { - if (!bodyPart.ContentLocation.IsAbsoluteUri) - continue; - - if (bodyPart.ContentLocation == uri) - return index; - } - } else if (bodyPart.ContentLocation == uri) { - return index; - } - } - - return -1; - } - - #region IEnumerable implementation - - /// - /// Get the body part enumerator. - /// - /// - /// Gets the body part enumerator. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return collection.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get the body part enumerator. - /// - /// - /// Gets the body part enumerator. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - } -} diff --git a/src/MailKit/BodyPartMessage.cs b/src/MailKit/BodyPartMessage.cs deleted file mode 100644 index 51985ba..0000000 --- a/src/MailKit/BodyPartMessage.cs +++ /dev/null @@ -1,124 +0,0 @@ -// -// BodyPartMessage.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -namespace MailKit { - /// - /// A message/rfc822 body part. - /// - /// - /// Represents a message/rfc822 body part. - /// - public class BodyPartMessage : BodyPartBasic - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public BodyPartMessage () - { - } - - /// - /// Gets the envelope of the message, if available. - /// - /// - /// Gets the envelope of the message, if available. - /// - /// The envelope. - public Envelope Envelope { - get; set; - } - - /// - /// Gets the body structure of the message. - /// - /// - /// Gets the body structure of the message. - /// - /// The body structure. - public BodyPart Body { - get; set; - } - - /// - /// Gets the length of the message, in lines. - /// - /// - /// Gets the length of the message, in lines. - /// - /// The number of lines. - public uint Lines { - get; set; - } - - /// - /// Dispatches to the specific visit method for this MIME body part. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (BodyPartVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitBodyPartMessage (this); - } - - /// - /// Encodes the into the . - /// - /// - /// Encodes the into the . - /// - /// The string builder. - protected override void Encode (StringBuilder builder) - { - base.Encode (builder); - - builder.Append (' '); - Encode (builder, Envelope); - builder.Append (' '); - Encode (builder, Body); - builder.Append (' '); - Encode (builder, Lines); - } - } -} diff --git a/src/MailKit/BodyPartMultipart.cs b/src/MailKit/BodyPartMultipart.cs deleted file mode 100644 index bcdc204..0000000 --- a/src/MailKit/BodyPartMultipart.cs +++ /dev/null @@ -1,141 +0,0 @@ -// -// BodyPartMultipart.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -using MimeKit; - -namespace MailKit { - /// - /// A multipart body part. - /// - /// - /// A multipart body part. - /// - public class BodyPartMultipart : BodyPart - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public BodyPartMultipart () - { - BodyParts = new BodyPartCollection (); - } - - /// - /// Gets the child body parts. - /// - /// - /// Gets the child body parts. - /// - /// The child body parts. - public BodyPartCollection BodyParts { - get; private set; - } - - /// - /// Gets the Content-Disposition of the body part, if available. - /// - /// - /// Gets the Content-Disposition of the body part, if available. - /// - /// The content disposition. - public ContentDisposition ContentDisposition { - get; set; - } - - /// - /// Gets the Content-Language of the body part, if available. - /// - /// - /// Gets the Content-Language of the body part, if available. - /// - /// The content language. - public string[] ContentLanguage { - get; set; - } - - /// - /// Gets the Content-Location of the body part, if available. - /// - /// - /// Gets the Content-Location of the body part, if available. - /// - /// The content location. - public Uri ContentLocation { - get; set; - } - - /// - /// Dispatches to the specific visit method for this MIME body part. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (BodyPartVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitBodyPartMultipart (this); - } - - /// - /// Encodes the into the . - /// - /// - /// Encodes the into the . - /// - /// The string builder. - protected override void Encode (StringBuilder builder) - { - Encode (builder, BodyParts); - builder.Append (' '); - Encode (builder, ContentType.MediaSubtype); - builder.Append (' '); - Encode (builder, ContentType.Parameters); - builder.Append (' '); - Encode (builder, ContentDisposition); - builder.Append (' '); - Encode (builder, ContentLanguage); - builder.Append (' '); - Encode (builder, ContentLocation); - } - } -} diff --git a/src/MailKit/BodyPartText.cs b/src/MailKit/BodyPartText.cs deleted file mode 100644 index 8cc9d90..0000000 --- a/src/MailKit/BodyPartText.cs +++ /dev/null @@ -1,123 +0,0 @@ -// -// BodyPartText.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -namespace MailKit { - /// - /// A textual body part. - /// - /// - /// Represents any body part with a media type of "text". - /// - /// - /// - /// - public class BodyPartText : BodyPartBasic - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public BodyPartText () - { - } - - /// - /// Gets whether or not this text part contains plain text. - /// - /// - /// Checks whether or not the text part's Content-Type is text/plain. - /// - /// true if the text is html; otherwise, false. - public bool IsPlain { - get { return ContentType.IsMimeType ("text", "plain"); } - } - - /// - /// Gets whether or not this text part contains HTML. - /// - /// - /// Checks whether or not the text part's Content-Type is text/html. - /// - /// true if the text is html; otherwise, false. - public bool IsHtml { - get { return ContentType.IsMimeType ("text", "html"); } - } - - /// - /// Gets the length of the text, in lines. - /// - /// - /// Gets the length of the text, in lines. - /// - /// The number of lines. - public uint Lines { - get; set; - } - - /// - /// Dispatches to the specific visit method for this MIME body part. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (BodyPartVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitBodyPartText (this); - } - - /// - /// Encodes the into the . - /// - /// - /// Encodes the into the . - /// - /// The string builder. - protected override void Encode (StringBuilder builder) - { - base.Encode (builder); - - builder.Append (' '); - Encode (builder, Lines); - } - } -} diff --git a/src/MailKit/BodyPartVisitor.cs b/src/MailKit/BodyPartVisitor.cs deleted file mode 100644 index 8c06173..0000000 --- a/src/MailKit/BodyPartVisitor.cs +++ /dev/null @@ -1,137 +0,0 @@ -// -// BodyPartVisitor.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit { - /// - /// Represents a visitor for a tree of MIME body parts. - /// - /// - /// This class is designed to be inherited to create more specialized classes whose - /// functionality requires traversing, examining or copying a tree of MIME body parts. - /// - public abstract class BodyPartVisitor - { - /// - /// Dispatches the entity to one of the more specialized visit methods in this class. - /// - /// - /// Dispatches the entity to one of the more specialized visit methods in this class. - /// - /// The MIME body part. - public virtual void Visit (BodyPart body) - { - if (body != null) - body.Accept (this); - } - - /// - /// Visit the abstract MIME body part. - /// - /// - /// Visits the abstract MIME body part. - /// - /// The MIME body part. - protected internal virtual void VisitBodyPart (BodyPart entity) - { - } - - /// - /// Visit the basic MIME body part. - /// - /// - /// Visits the basic MIME body part. - /// - /// The basic MIME body part. - protected internal virtual void VisitBodyPartBasic (BodyPartBasic entity) - { - VisitBodyPart (entity); - } - - /// - /// Visit the message contained within a message/rfc822 or message/news MIME entity. - /// - /// - /// Visits the message contained within a message/rfc822 or message/news MIME entity. - /// - /// The body part representing the message/rfc822 message. - protected virtual void VisitMessage (BodyPart message) - { - if (message != null) - message.Accept (this); - } - - /// - /// Visit the message/rfc822 or message/news MIME entity. - /// - /// - /// Visits the message/rfc822 or message/news MIME entity. - /// - /// The message/rfc822 or message/news body part. - protected internal virtual void VisitBodyPartMessage (BodyPartMessage entity) - { - VisitBodyPartBasic (entity); - VisitMessage (entity.Body); - } - - /// - /// Visit the children of a . - /// - /// - /// Visits the children of a . - /// - /// The multipart. - protected virtual void VisitChildren (BodyPartMultipart multipart) - { - for (int i = 0; i < multipart.BodyParts.Count; i++) - multipart.BodyParts[i].Accept (this); - } - - /// - /// Visit the abstract multipart MIME entity. - /// - /// - /// Visits the abstract multipart MIME entity. - /// - /// The multipart body part. - protected internal virtual void VisitBodyPartMultipart (BodyPartMultipart multipart) - { - VisitBodyPart (multipart); - VisitChildren (multipart); - } - - /// - /// Visit the text-based MIME part entity. - /// - /// - /// Visits the text-based MIME part entity. - /// - /// The text-based body part. - protected internal virtual void VisitBodyPartText (BodyPartText entity) - { - VisitBodyPartBasic (entity); - } - } -} diff --git a/src/MailKit/CommandException.cs b/src/MailKit/CommandException.cs deleted file mode 100644 index b47bbfe..0000000 --- a/src/MailKit/CommandException.cs +++ /dev/null @@ -1,102 +0,0 @@ -// -// CommandException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit { - /// - /// The exception that is thrown when there is a command error. - /// - /// - /// A can be thrown by any of the various client - /// methods in MailKit. Unlike a , a - /// is typically non-fatal (meaning that it does - /// not force the client to disconnect). - /// -#if SERIALIZABLE - [Serializable] -#endif - public abstract class CommandException : Exception - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected CommandException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// An inner exception. - protected CommandException (string message, Exception innerException) : base (message, innerException) - { - HelpLink = "https://github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLog"; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - protected CommandException (string message) : base (message) - { - HelpLink = "https://github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLog"; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - protected CommandException () - { - HelpLink = "https://github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLog"; - } - } -} diff --git a/src/MailKit/CompressedStream.cs b/src/MailKit/CompressedStream.cs deleted file mode 100644 index 1de9858..0000000 --- a/src/MailKit/CompressedStream.cs +++ /dev/null @@ -1,435 +0,0 @@ -// -// CompressedStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -using Org.BouncyCastle.Utilities.Zlib; - -namespace MailKit { - /// - /// A compressed stream. - /// - class CompressedStream : Stream - { - readonly ZStream zIn, zOut; - bool eos, disposed; - - public CompressedStream (Stream innerStream) - { - InnerStream = innerStream; - - zOut = new ZStream (); - zOut.deflateInit (5, true); - zOut.next_out = new byte[4096]; - - zIn = new ZStream (); - zIn.inflateInit (true); - zIn.next_in = new byte[4096]; - } - - /// - /// Gets the inner stream. - /// - /// The inner stream. - public Stream InnerStream { - get; private set; - } - - /// - /// Gets whether the stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return InnerStream.CanRead; } - } - - /// - /// Gets whether the stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return InnerStream.CanWrite; } - } - - /// - /// Gets whether the stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return false; } - } - - /// - /// Gets whether the stream supports I/O timeouts. - /// - /// true if the stream supports I/O timeouts; otherwise, false. - public override bool CanTimeout { - get { return InnerStream.CanTimeout; } - } - - /// - /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// A value, in miliseconds, that determines how long the stream will attempt to read before timing out. - /// The read timeout. - public override int ReadTimeout { - get { return InnerStream.ReadTimeout; } - set { InnerStream.ReadTimeout = value; } - } - - /// - /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// A value, in miliseconds, that determines how long the stream will attempt to write before timing out. - /// The write timeout. - public override int WriteTimeout { - get { return InnerStream.WriteTimeout; } - set { InnerStream.WriteTimeout = value; } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// The position of the stream. - /// - /// The stream does not support seeking. - /// - public override long Position { - get { throw new NotSupportedException (); } - set { throw new NotSupportedException (); } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// A long value representing the length of the stream in bytes. - /// The length of the stream. - /// - /// The stream does not support seeking. - /// - public override long Length { - get { throw new NotSupportedException (); } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (CompressedStream)); - } - - async Task ReadAsync (byte[] buffer, int offset, int count, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - if (count == 0) - return 0; - - zIn.next_out = buffer; - zIn.next_out_index = offset; - zIn.avail_out = count; - - do { - if (zIn.avail_in == 0 && !eos) { - cancellationToken.ThrowIfCancellationRequested (); - - if (doAsync) - zIn.avail_in = await InnerStream.ReadAsync (zIn.next_in, 0, zIn.next_in.Length, cancellationToken).ConfigureAwait (false); - else - zIn.avail_in = InnerStream.Read (zIn.next_in, 0, zIn.next_in.Length); - - eos = zIn.avail_in == 0; - zIn.next_in_index = 0; - } - - int retval = zIn.inflate (JZlib.Z_FULL_FLUSH); - - if (retval == JZlib.Z_STREAM_END) - break; - - if (eos && retval == JZlib.Z_BUF_ERROR) - return 0; - - if (retval != JZlib.Z_OK) - throw new IOException ("Error inflating: " + zIn.msg); - } while (zIn.avail_out == count); - - return count - zIn.avail_out; - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - return ReadAsync (buffer, offset, count, false, CancellationToken.None).GetAwaiter ().GetResult (); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// An I/O error occurred. - /// - public override Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return ReadAsync (buffer, offset, count, true, cancellationToken); - } - - async Task WriteAsync (byte[] buffer, int offset, int count, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - if (count == 0) - return; - - zOut.next_in = buffer; - zOut.next_in_index = offset; - zOut.avail_in = count; - - do { - cancellationToken.ThrowIfCancellationRequested (); - - zOut.avail_out = zOut.next_out.Length; - zOut.next_out_index = 0; - - if (zOut.deflate (JZlib.Z_FULL_FLUSH) != JZlib.Z_OK) - throw new IOException ("Error deflating: " + zOut.msg); - - if (doAsync) - await InnerStream.WriteAsync (zOut.next_out, 0, zOut.next_out.Length - zOut.avail_out, cancellationToken).ConfigureAwait (false); - else - InnerStream.Write (zOut.next_out, 0, zOut.next_out.Length - zOut.avail_out); - } while (zOut.avail_in > 0 || zOut.avail_out == 0); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - WriteAsync (buffer, offset, count, false, CancellationToken.None).GetAwaiter ().GetResult (); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return WriteAsync (buffer, offset, count, true, cancellationToken); - } - - /// - /// Clears all output buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Flush () - { - CheckDisposed (); - - InnerStream.Flush (); - } - - /// - /// Clears all output buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// A task that represents the asynchronous flush operation. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override Task FlushAsync (CancellationToken cancellationToken) - { - CheckDisposed (); - - return InnerStream.FlushAsync (cancellationToken); - } - - /// - /// Sets the position within the current stream. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// The stream does not support seeking. - /// - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - /// - /// Sets the length of the stream. - /// - /// The desired length of the stream in bytes. - /// - /// The stream does not support setting the length. - /// - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - InnerStream.Dispose (); - disposed = true; - zOut.free (); - zIn.free (); - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MailKit/ConnectedEventArgs.cs b/src/MailKit/ConnectedEventArgs.cs deleted file mode 100644 index 99fc534..0000000 --- a/src/MailKit/ConnectedEventArgs.cs +++ /dev/null @@ -1,88 +0,0 @@ -// -// ConnectedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MailKit.Security; - -namespace MailKit -{ - /// - /// Connected event arguments. - /// - /// - /// When a is connected, it will emit a - /// event. - /// - public class ConnectedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// The name of the host that the client connected to. - /// The port that the client connected to on the remote host. - /// The SSL/TLS options that were used when connecting to the remote host. - public ConnectedEventArgs (string host, int port, SecureSocketOptions options) - { - Options = options; - Host = host; - Port = port; - } - - /// - /// Get the name of the remote host. - /// - /// - /// Gets the name of the remote host. - /// - /// The host name of the server. - public string Host { - get; private set; - } - - /// - /// Get the port. - /// - /// - /// Gets the port. - /// - /// The port. - public int Port { - get; private set; - } - - /// - /// Get the SSL/TLS options. - /// - /// - /// Gets the SSL/TLS options. - /// - /// The SSL/TLS options. - public SecureSocketOptions Options { - get; private set; - } - } -} diff --git a/src/MailKit/DeliveryStatusNotification.cs b/src/MailKit/DeliveryStatusNotification.cs deleted file mode 100644 index 72c2823..0000000 --- a/src/MailKit/DeliveryStatusNotification.cs +++ /dev/null @@ -1,61 +0,0 @@ -// -// DeliveryStatusNotification.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Delivery status notification types. - /// - /// - /// A set of flags that may be bitwise-or'd together to specify - /// when a delivery status notification should be sent for a - /// particlar recipient. - /// - [Flags] - public enum DeliveryStatusNotification { - /// - /// Never send delivery status notifications. - /// - Never = 0, - - /// - /// Send a notification on successful delivery to the recipient. - /// - Success = (1 << 0), - - /// - /// Send a notification on failure to deliver to the recipient. - /// - Failure = (1 << 1), - - /// - /// Send a notification when the delivery to the recipient has - /// been delayed for an unusual amount of time. - /// - Delay = (1 << 2) - } -} diff --git a/src/MailKit/DeliveryStatusNotificationType.cs b/src/MailKit/DeliveryStatusNotificationType.cs deleted file mode 100644 index 717404b..0000000 --- a/src/MailKit/DeliveryStatusNotificationType.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// DeliveryStatusNotificationReturnType.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2019 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Net.Smtp -{ - /// - /// Delivery status notification type. - /// - /// - /// The delivery status notification type specifies whether or not - /// the full message should be included in any failed DSN issued for - /// a message transmission as opposed to just the headers. - /// - public enum DeliveryStatusNotificationType - { - /// - /// The return type is unspecified, allowing the server to choose. - /// - Unspecified, - - /// - /// The full message should be included in any failed delivery status notification issued by the server. - /// - Full, - - /// - /// Only the headers should be included in any failed delivery status notification issued by the server. - /// - HeadersOnly, - } -} diff --git a/src/MailKit/DisconnectedEventArgs.cs b/src/MailKit/DisconnectedEventArgs.cs deleted file mode 100644 index 676c904..0000000 --- a/src/MailKit/DisconnectedEventArgs.cs +++ /dev/null @@ -1,70 +0,0 @@ -// -// DisconnectedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MailKit.Security; - -namespace MailKit -{ - /// - /// Disconnected event arguments. - /// - /// - /// When a gets disconnected, it will emit a - /// event. - /// - public class DisconnectedEventArgs : ConnectedEventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The name of the host that the client was connected to. - /// The port that the client was connected to. - /// The SSL/TLS options that were used by the client. - /// If true, the was disconnected via the - /// method. - public DisconnectedEventArgs (string host, int port, SecureSocketOptions options, bool requested) : base (host, port, options) - { - IsRequested = requested; - } - - /// - /// Get whether or not the service was explicitly asked to disconnect. - /// - /// - /// If the was disconnected via the - /// method, then - /// the value of will be true. If the connection was unexpectedly - /// dropped, then the value will be false. - /// - /// true if the disconnect was explicitly requested; otherwise, false. - public bool IsRequested { - get; private set; - } - } -} diff --git a/src/MailKit/DuplexStream.cs b/src/MailKit/DuplexStream.cs deleted file mode 100644 index e71ab1e..0000000 --- a/src/MailKit/DuplexStream.cs +++ /dev/null @@ -1,395 +0,0 @@ -// -// DuplexStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MailKit { - /// - /// A duplex stream. - /// - class DuplexStream : Stream - { - bool disposed; - - /// - /// Initializes a new instance of the class. - /// - /// The stream to use for input. - /// The stream to use for output. - /// - /// is null. - /// -or- - /// is null. - /// - public DuplexStream (Stream istream, Stream ostream) - { - if (istream == null) - throw new ArgumentNullException (nameof (istream)); - - if (ostream == null) - throw new ArgumentNullException (nameof (ostream)); - - InputStream = istream; - OutputStream = ostream; - } - - /// - /// Gets the input stream. - /// - /// The input stream. - public Stream InputStream { - get; private set; - } - - /// - /// Gets the output stream. - /// - /// The output stream. - public Stream OutputStream { - get; private set; - } - - /// - /// Gets whether the stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return true; } - } - - /// - /// Gets whether the stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return true; } - } - - /// - /// Gets whether the stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return false; } - } - - /// - /// Gets whether the stream supports I/O timeouts. - /// - /// true if the stream supports I/O timeouts; otherwise, false. - public override bool CanTimeout { - get { return InputStream.CanTimeout && OutputStream.CanTimeout; } - } - - /// - /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// A value, in miliseconds, that determines how long the stream will attempt to read before timing out. - /// The read timeout. - public override int ReadTimeout { - get { return InputStream.ReadTimeout; } - set { InputStream.ReadTimeout = value; } - } - - /// - /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// A value, in miliseconds, that determines how long the stream will attempt to write before timing out. - /// The write timeout. - public override int WriteTimeout { - get { return OutputStream.WriteTimeout; } - set { OutputStream.WriteTimeout = value; } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// The position of the stream. - /// - /// The stream does not support seeking. - /// - public override long Position { - get { throw new NotSupportedException (); } - set { throw new NotSupportedException (); } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// A long value representing the length of the stream in bytes. - /// The length of the stream. - /// - /// The stream does not support seeking. - /// - public override long Length { - get { throw new NotSupportedException (); } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (DuplexStream)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - return InputStream.Read (buffer, offset, count); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// An I/O error occurred. - /// - public override Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - return InputStream.ReadAsync (buffer, offset, count, cancellationToken); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - OutputStream.Write (buffer, offset, count); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - return OutputStream.WriteAsync (buffer, offset, count, cancellationToken); - } - - /// - /// Clears all output buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Flush () - { - CheckDisposed (); - - OutputStream.Flush (); - } - - /// - /// Clears all output buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override Task FlushAsync (CancellationToken cancellationToken) - { - CheckDisposed (); - - return OutputStream.FlushAsync (cancellationToken); - } - - /// - /// Sets the position within the current stream. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// The stream does not support seeking. - /// - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - /// - /// Sets the length of the stream. - /// - /// The desired length of the stream in bytes. - /// - /// The stream does not support setting the length. - /// - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - OutputStream.Dispose (); - InputStream.Dispose (); - disposed = true; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MailKit/Envelope.cs b/src/MailKit/Envelope.cs deleted file mode 100644 index 76366cc..0000000 --- a/src/MailKit/Envelope.cs +++ /dev/null @@ -1,592 +0,0 @@ -// -// Envelope.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Linq; -using System.Collections.Generic; - -using MimeKit; -using MimeKit.Utils; - -namespace MailKit { - /// - /// A message envelope containing a brief summary of the message. - /// - /// - /// The envelope of a message contains information such as the - /// date the message was sent, the subject of the message, - /// the sender of the message, who the message was sent to, - /// which message(s) the message may be in reply to, - /// and the message id. - /// - public class Envelope - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public Envelope () - { - From = new InternetAddressList (); - Sender = new InternetAddressList (); - ReplyTo = new InternetAddressList (); - To = new InternetAddressList (); - Cc = new InternetAddressList (); - Bcc = new InternetAddressList (); - } - - /// - /// Gets the address(es) that the message is from. - /// - /// - /// Gets the address(es) that the message is from. - /// - /// The address(es) that the message is from. - public InternetAddressList From { - get; private set; - } - - /// - /// Gets the actual sender(s) of the message. - /// - /// - /// The senders may differ from the addresses in if - /// the message was sent by someone on behalf of someone else. - /// - /// The actual sender(s) of the message. - public InternetAddressList Sender { - get; private set; - } - - /// - /// Gets the address(es) that replies should be sent to. - /// - /// - /// The senders of the message may prefer that replies are sent - /// somewhere other than the address they used to send the message. - /// - /// The address(es) that replies should be sent to. - public InternetAddressList ReplyTo { - get; private set; - } - - /// - /// Gets the list of addresses that the message was sent to. - /// - /// - /// Gets the list of addresses that the message was sent to. - /// - /// The address(es) that the message was sent to. - public InternetAddressList To { - get; private set; - } - - /// - /// Gets the list of addresses that the message was carbon-copied to. - /// - /// - /// Gets the list of addresses that the message was carbon-copied to. - /// - /// The address(es) that the message was carbon-copied to. - public InternetAddressList Cc { - get; private set; - } - - /// - /// Gets the list of addresses that the message was blind-carbon-copied to. - /// - /// - /// Gets the list of addresses that the message was blind-carbon-copied to. - /// - /// The address(es) that the message was carbon-copied to. - public InternetAddressList Bcc { - get; private set; - } - - /// - /// The Message-Id that the message is replying to. - /// - /// - /// The Message-Id that the message is replying to. - /// - /// The Message-Id that the message is replying to. - public string InReplyTo { - get; set; - } - - /// - /// Gets the date that the message was sent on, if available. - /// - /// - /// Gets the date that the message was sent on, if available. - /// - /// The date the message was sent. - public DateTimeOffset? Date { - get; set; - } - - /// - /// Gets the ID of the message, if available. - /// - /// - /// Gets the ID of the message, if available. - /// - /// The message identifier. - public string MessageId { - get; set; - } - - /// - /// Gets the subject of the message. - /// - /// - /// Gets the subject of the message. - /// - /// The subject. - public string Subject { - get; set; - } - - static void EncodeMailbox (StringBuilder builder, MailboxAddress mailbox) - { - builder.Append ('('); - - if (mailbox.Name != null) - builder.AppendFormat ("{0} ", MimeUtils.Quote (mailbox.Name)); - else - builder.Append ("NIL "); - - if (mailbox.Route.Count != 0) - builder.AppendFormat ("\"{0}\" ", mailbox.Route); - else - builder.Append ("NIL "); - - int at = mailbox.Address.LastIndexOf ('@'); - - if (at >= 0) { - var domain = mailbox.Address.Substring (at + 1); - var user = mailbox.Address.Substring (0, at); - - builder.AppendFormat ("{0} {1}", MimeUtils.Quote (user), MimeUtils.Quote (domain)); - } else { - builder.AppendFormat ("{0} \"localhost\"", MimeUtils.Quote (mailbox.Address)); - } - - builder.Append (')'); - } - - static void EncodeInternetAddressListAddresses (StringBuilder builder, InternetAddressList addresses) - { - foreach (var addr in addresses) { - var mailbox = addr as MailboxAddress; - var group = addr as GroupAddress; - - if (mailbox != null) - EncodeMailbox (builder, mailbox); - else if (group != null) - EncodeGroup (builder, group); - } - } - - static void EncodeGroup (StringBuilder builder, GroupAddress group) - { - builder.AppendFormat ("(NIL NIL {0} NIL)", MimeUtils.Quote (group.Name)); - EncodeInternetAddressListAddresses (builder, group.Members); - builder.Append ("(NIL NIL NIL NIL)"); - } - - static void EncodeAddressList (StringBuilder builder, InternetAddressList list) - { - builder.Append ('('); - EncodeInternetAddressListAddresses (builder, list); - builder.Append (')'); - } - - internal void Encode (StringBuilder builder) - { - builder.Append ('('); - - if (Date.HasValue) - builder.AppendFormat ("\"{0}\" ", DateUtils.FormatDate (Date.Value)); - else - builder.Append ("NIL "); - - if (Subject != null) - builder.AppendFormat ("{0} ", MimeUtils.Quote (Subject)); - else - builder.Append ("NIL "); - - if (From.Count > 0) { - EncodeAddressList (builder, From); - builder.Append (' '); - } else { - builder.Append ("NIL "); - } - - if (Sender.Count > 0) { - EncodeAddressList (builder, Sender); - builder.Append (' '); - } else { - builder.Append ("NIL "); - } - - if (ReplyTo.Count > 0) { - EncodeAddressList (builder, ReplyTo); - builder.Append (' '); - } else { - builder.Append ("NIL "); - } - - if (To.Count > 0) { - EncodeAddressList (builder, To); - builder.Append (' '); - } else { - builder.Append ("NIL "); - } - - if (Cc.Count > 0) { - EncodeAddressList (builder, Cc); - builder.Append (' '); - } else { - builder.Append ("NIL "); - } - - if (Bcc.Count > 0) { - EncodeAddressList (builder, Bcc); - builder.Append (' '); - } else { - builder.Append ("NIL "); - } - - if (InReplyTo != null) { - if (InReplyTo.Length > 1 && InReplyTo[0] != '<' && InReplyTo[InReplyTo.Length - 1] != '>') - builder.AppendFormat ("{0} ", MimeUtils.Quote ('<' + InReplyTo + '>')); - else - builder.AppendFormat ("{0} ", MimeUtils.Quote (InReplyTo)); - } else - builder.Append ("NIL "); - - if (MessageId != null) { - if (MessageId.Length > 1 && MessageId[0] != '<' && MessageId[MessageId.Length - 1] != '>') - builder.AppendFormat ("{0}", MimeUtils.Quote ('<' + MessageId + '>')); - else - builder.AppendFormat ("{0}", MimeUtils.Quote (MessageId)); - } else - builder.Append ("NIL"); - - builder.Append (')'); - } - - /// - /// Returns a that represents the current . - /// - /// - /// The returned string can be parsed by . - /// The syntax of the string returned, while similar to IMAP's ENVELOPE syntax, - /// is not completely compatible. - /// - /// A that represents the current . - public override string ToString () - { - var builder = new StringBuilder (); - - Encode (builder); - - return builder.ToString (); - } - - static bool TryParse (string text, ref int index, out string nstring) - { - nstring = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length) - return false; - - if (text[index] != '"') { - if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") { - index += 3; - return true; - } - - return false; - } - - var token = new StringBuilder (); - bool escaped = false; - - index++; - - while (index < text.Length) { - if (text[index] == '"' && !escaped) - break; - - if (escaped || text[index] != '\\') { - token.Append (text[index]); - escaped = false; - } else { - escaped = true; - } - - index++; - } - - if (index >= text.Length) - return false; - - nstring = token.ToString (); - - index++; - - return true; - } - - static bool TryParse (string text, ref int index, out InternetAddress addr) - { - string name, route, user, domain; - DomainList domains; - - addr = null; - - if (text[index] != '(') - return false; - - index++; - - if (!TryParse (text, ref index, out name)) - return false; - - if (!TryParse (text, ref index, out route)) - return false; - - if (!TryParse (text, ref index, out user)) - return false; - - if (!TryParse (text, ref index, out domain)) - return false; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length || text[index] != ')') - return false; - - index++; - - if (domain != null) { - var address = user + "@" + domain; - - if (route != null && DomainList.TryParse (route, out domains)) - addr = new MailboxAddress (name, domains, address); - else - addr = new MailboxAddress (name, address); - } else if (user != null) { - addr = new GroupAddress (user); - } - - return true; - } - - static bool TryParse (string text, ref int index, out InternetAddressList list) - { - list = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length) - return false; - - if (text[index] != '(') { - if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") { - list = new InternetAddressList (); - index += 3; - return true; - } - - return false; - } - - index++; - - if (index >= text.Length) - return false; - - list = new InternetAddressList (); - var stack = new List (); - int sp = 0; - - stack.Add (list); - - do { - if (text[index] == ')') - break; - - if (!TryParse (text, ref index, out InternetAddress addr)) - return false; - - if (addr != null) { - var group = addr as GroupAddress; - - stack[sp].Add (addr); - - if (group != null) { - stack.Add (group.Members); - sp++; - } - } else if (sp > 0) { - stack.RemoveAt (sp); - sp--; - } - - while (index < text.Length && text[index] == ' ') - index++; - } while (index < text.Length); - - // Note: technically, we should check that sp == 0 as well, since all groups should - // be popped off the stack, but in the interest of being liberal in what we accept, - // we'll ignore that. - if (index >= text.Length) - return false; - - index++; - - return true; - } - - internal static bool TryParse (string text, ref int index, out Envelope envelope) - { - InternetAddressList from, sender, replyto, to, cc, bcc; - string inreplyto, messageid, subject, nstring; - DateTimeOffset? date = null; - - envelope = null; - - while (index < text.Length && text[index] == ' ') - index++; - - if (index >= text.Length || text[index] != '(') { - if (index + 3 <= text.Length && text.Substring (index, 3) == "NIL") { - index += 3; - return true; - } - - return false; - } - - index++; - - if (!TryParse (text, ref index, out nstring)) - return false; - - if (nstring != null) { - DateTimeOffset value; - - if (!DateUtils.TryParse (nstring, out value)) - return false; - - date = value; - } - - if (!TryParse (text, ref index, out subject)) - return false; - - if (!TryParse (text, ref index, out from)) - return false; - - if (!TryParse (text, ref index, out sender)) - return false; - - if (!TryParse (text, ref index, out replyto)) - return false; - - if (!TryParse (text, ref index, out to)) - return false; - - if (!TryParse (text, ref index, out cc)) - return false; - - if (!TryParse (text, ref index, out bcc)) - return false; - - if (!TryParse (text, ref index, out inreplyto)) - return false; - - if (!TryParse (text, ref index, out messageid)) - return false; - - if (index >= text.Length || text[index] != ')') - return false; - - index++; - - envelope = new Envelope { - Date = date, - Subject = subject, - From = from, - Sender = sender, - ReplyTo = replyto, - To = to, - Cc = cc, - Bcc = bcc, - InReplyTo = inreplyto != null ? MimeUtils.EnumerateReferences (inreplyto).FirstOrDefault () ?? inreplyto : null, - MessageId = messageid != null ? MimeUtils.EnumerateReferences (messageid).FirstOrDefault () ?? messageid : null - }; - - return true; - } - - /// - /// Tries to parse the given text into a new instance. - /// - /// - /// Parses an Envelope value from the specified text. - /// This syntax, while similar to IMAP's ENVELOPE syntax, is not - /// completely compatible. - /// - /// true, if the envelope was successfully parsed, false otherwise. - /// The text to parse. - /// The parsed envelope. - /// - /// is null. - /// - public static bool TryParse (string text, out Envelope envelope) - { - if (text == null) - throw new ArgumentNullException (nameof (text)); - - int index = 0; - - return TryParse (text, ref index, out envelope) && index == text.Length; - } - } -} - \ No newline at end of file diff --git a/src/MailKit/FolderAccess.cs b/src/MailKit/FolderAccess.cs deleted file mode 100644 index 940b4e0..0000000 --- a/src/MailKit/FolderAccess.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// FolderMode.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit { - /// - /// A folder access mode. - /// - /// - /// A folder access mode. - /// - /// - /// - /// - public enum FolderAccess { - /// - /// The folder is not open. - /// - None, - - /// - /// The folder is read-only. - /// - ReadOnly, - - /// - /// The folder is read/write. - /// - ReadWrite - } -} diff --git a/src/MailKit/FolderAttributes.cs b/src/MailKit/FolderAttributes.cs deleted file mode 100644 index e87ebca..0000000 --- a/src/MailKit/FolderAttributes.cs +++ /dev/null @@ -1,135 +0,0 @@ -// -// FolderAttributes.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Folder attributes as used by . - /// - /// - /// Folder attributes as used by . - /// - [Flags] - public enum FolderAttributes { - /// - /// The folder does not have any attributes. - /// - None = 0, - - /// - /// It is not possible for any subfolders to exist under the folder. - /// - NoInferiors = (1 << 0), - - /// - /// It is not possible to select the folder. - /// - NoSelect = (1 << 1), - - /// - /// The folder has been marked as possibly containing new messages - /// since the folder was last selected. - /// - Marked = (1 << 2), - - /// - /// The folder does not contain any new messages since the folder - /// was last selected. - /// - Unmarked = (1 << 3), - - /// - /// The folder does not exist, but is simply a place-holder. - /// - NonExistent = (1 << 4), - - /// - /// The folder is subscribed. - /// - Subscribed = (1 << 5), - - /// - /// The folder is remote. - /// - Remote = (1 << 6), - - /// - /// The folder has subfolders. - /// - HasChildren = (1 << 7), - - /// - /// The folder does not have any subfolders. - /// - HasNoChildren = (1 << 8), - - /// - /// The folder is a special "All" folder containing an aggregate of all messages. - /// - All = (1 << 9), - - /// - /// The folder is a special "Archive" folder. - /// - Archive = (1 << 10), - - /// - /// The folder is the special "Drafts" folder. - /// - Drafts = (1 << 11), - - /// - /// The folder is the special "Flagged" folder. - /// - Flagged = (1 << 12), - - /// - /// The folder is the special "Important" folder. - /// - Important = (1 << 13), - - /// - /// The folder is the special "Inbox" folder. - /// - Inbox = (1 << 14), - - /// - /// The folder is the special "Junk" folder. - /// - Junk = (1 << 15), - - /// - /// The folder is the special "Sent" folder. - /// - Sent = (1 << 16), - - /// - /// The folder is the special "Trash" folder. - /// - Trash = (1 << 17), - } -} diff --git a/src/MailKit/FolderCreatedEventArgs.cs b/src/MailKit/FolderCreatedEventArgs.cs deleted file mode 100644 index 949a1ad..0000000 --- a/src/MailKit/FolderCreatedEventArgs.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// FolderCreatedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Event args used when a is created. - /// - /// - /// Event args used when a is created. - /// - public class FolderCreatedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The newly created folder. - /// - /// is null. - /// - public FolderCreatedEventArgs (IMailFolder folder) - { - if (folder == null) - throw new ArgumentNullException (nameof (folder)); - - Folder = folder; - } - - /// - /// Get the folder that was just created. - /// - /// - /// Gets the folder that was just created. - /// - /// The folder. - public IMailFolder Folder { - get; private set; - } - } -} diff --git a/src/MailKit/FolderFeature.cs b/src/MailKit/FolderFeature.cs deleted file mode 100644 index f9774fa..0000000 --- a/src/MailKit/FolderFeature.cs +++ /dev/null @@ -1,82 +0,0 @@ -// -// FolderFeature.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit -{ - /// - /// An optional feature that an may support. - /// - /// - /// An optional feature that an may support. - /// - public enum FolderFeature - { - /// - /// Indicates that the folder supports access rights. - /// - AccessRights, - - /// - /// Indicates that the folder allows arbitrary annotations to be set on a message. - /// - Annotations, - - /// - /// Indicates that the folder allows arbitrary metadata to be set. - /// - Metadata, - - /// - /// Indicates that the folder uses modification sequences for every state change of a message. - /// - ModSequences, - - /// - /// Indicates that the folder supports quick resynchronization when opening. - /// - QuickResync, - - /// - /// Indicates that the folder supports quotas. - /// - Quotas, - - /// - /// Indicates that the folder supports sorting messages. - /// - Sorting, - - /// - /// Indicates that the folder supports threading messages. - /// - Threading, - - /// - /// Indicates that the folder supports the use of UTF-8. - /// - UTF8, - } -} diff --git a/src/MailKit/FolderNamespace.cs b/src/MailKit/FolderNamespace.cs deleted file mode 100644 index b1cb46e..0000000 --- a/src/MailKit/FolderNamespace.cs +++ /dev/null @@ -1,74 +0,0 @@ -// -// FolderNamespace.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// A folder namespace. - /// - /// - /// A folder namespace. - /// - public class FolderNamespace - { - /// - /// The directory separator for this folder namespace. - /// - /// - /// The directory separator for this folder namespace. - /// - public readonly char DirectorySeparator; - - /// - /// The base path for this folder namespace. - /// - /// - /// The base path for this folder namespace. - /// - public readonly string Path; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new folder namespace. - /// - /// The directory separator. - /// The folder path. - /// - /// is null. - /// - public FolderNamespace (char directorySeparator, string path) - { - if (path == null) - throw new ArgumentNullException (nameof (path)); - - DirectorySeparator = directorySeparator; - Path = path; - } - } -} diff --git a/src/MailKit/FolderNamespaceCollection.cs b/src/MailKit/FolderNamespaceCollection.cs deleted file mode 100644 index c432b39..0000000 --- a/src/MailKit/FolderNamespaceCollection.cs +++ /dev/null @@ -1,235 +0,0 @@ -// -// FolderNamespaceCollection.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MailKit { - /// - /// A read-only collection of folder namespaces. - /// - /// - /// A read-only collection of folder namespaces. - /// - public class FolderNamespaceCollection : IEnumerable - { - readonly List namespaces; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public FolderNamespaceCollection () - { - namespaces = new List (); - } - - #region ICollection implementation - - /// - /// Gets the number of folder namespaces contained in the collection. - /// - /// - /// Gets the number of folder namespaces contained in the collection. - /// - /// The count. - public int Count { - get { return namespaces.Count; } - } - - /// - /// Adds the specified namespace. - /// - /// - /// Adds the specified namespace. - /// - /// The namespace to add. - /// - /// is null. - /// - public void Add (FolderNamespace @namespace) - { - if (@namespace == null) - throw new ArgumentNullException (nameof (@namespace)); - - namespaces.Add (@namespace); - } - - /// - /// Removes all namespaces from the collection. - /// - /// - /// Removes all namespaces from the collection. - /// - public void Clear () - { - namespaces.Clear (); - } - - /// - /// Checks if the collection contains the specified namespace. - /// - /// - /// Checks if the collection contains the specified namespace. - /// - /// true if the specified namespace exists; - /// otherwise false. - /// The namespace. - /// - /// is null. - /// - public bool Contains (FolderNamespace @namespace) - { - if (@namespace == null) - throw new ArgumentNullException (nameof (@namespace)); - - return namespaces.Contains (@namespace); - } - - /// - /// Removes the first occurance of the specified namespace. - /// - /// - /// Removes the first occurance of the specified namespace. - /// - /// true if the frst occurance of the specified - /// namespace was removed; otherwise false. - /// The namespace. - /// - /// is null. - /// - public bool Remove (FolderNamespace @namespace) - { - if (@namespace == null) - throw new ArgumentNullException (nameof (@namespace)); - - return namespaces.Remove (@namespace); - } - - /// - /// Gets the at the specified index. - /// - /// - /// Gets the at the specified index. - /// - /// The folder namespace at the specified index. - /// The index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public FolderNamespace this [int index] { - get { - if (index < 0 || index >= namespaces.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - return namespaces[index]; - } - set { - if (index < 0 || index >= namespaces.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - namespaces[index] = value; - } - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets the enumerator. - /// - /// - /// Gets the enumerator. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return namespaces.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets the enumerator. - /// - /// - /// Gets the enumerator. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return namespaces.GetEnumerator (); - } - - #endregion - - static bool Escape (char directorySeparator) - { - return directorySeparator == '\\' || directorySeparator == '"'; - } - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - var builder = new StringBuilder (); - - builder.Append ('('); - for (int i = 0; i < namespaces.Count; i++) { - builder.Append ("(\""); - if (Escape (namespaces[i].DirectorySeparator)) - builder.Append ('\\'); - builder.Append (namespaces[i].DirectorySeparator); - builder.Append ("\" "); - builder.Append (MimeUtils.Quote (namespaces[i].Path)); - builder.Append (")"); - } - builder.Append (')'); - - return builder.ToString (); - } - } -} diff --git a/src/MailKit/FolderNotFoundException.cs b/src/MailKit/FolderNotFoundException.cs deleted file mode 100644 index 9747e95..0000000 --- a/src/MailKit/FolderNotFoundException.cs +++ /dev/null @@ -1,149 +0,0 @@ -// -// FolderNotFoundException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit { - /// - /// The exception that is thrown when a folder could not be found. - /// - /// - /// This exception is thrown by . - /// -#if SERIALIZABLE - [Serializable] -#endif - public class FolderNotFoundException : Exception - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Deserializes a . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - protected FolderNotFoundException (SerializationInfo info, StreamingContext context) : base (info, context) - { - FolderName = info.GetString ("FolderName"); - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The name of the folder. - /// The inner exception. - /// - /// is null. - /// - public FolderNotFoundException (string message, string folderName, Exception innerException) : base (message, innerException) - { - if (folderName == null) - throw new ArgumentNullException (nameof (folderName)); - - FolderName = folderName; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The name of the folder. - /// - /// is null. - /// - public FolderNotFoundException (string message, string folderName) : base (message) - { - if (folderName == null) - throw new ArgumentNullException (nameof (folderName)); - - FolderName = folderName; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The name of the folder. - /// - /// is null. - /// - public FolderNotFoundException (string folderName) : this ("The requested folder could not be found.", folderName) - { - } - - /// - /// Gets the name of the folder that could not be found. - /// - /// - /// Gets the name of the folder that could not be found. - /// - /// The name of the folder. - public string FolderName { - get; private set; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Serializes the state of the . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("FolderName", FolderName); - } -#endif - } -} diff --git a/src/MailKit/FolderNotOpenException.cs b/src/MailKit/FolderNotOpenException.cs deleted file mode 100644 index 1a52d99..0000000 --- a/src/MailKit/FolderNotOpenException.cs +++ /dev/null @@ -1,184 +0,0 @@ -// -// FolderNotOpenException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Runtime.Serialization; -#endif - -namespace MailKit { - /// - /// The exception that is thrown when a folder is not open. - /// - /// - /// This exception is thrown when an operation on a folder could not be completed - /// due to the folder being in a closed state. For example, the - /// - /// method will throw a if the folder is not - /// current open. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class FolderNotOpenException : InvalidOperationException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Deserializes a . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - protected FolderNotOpenException (SerializationInfo info, StreamingContext context) : base (info, context) - { - var value = info.GetString ("FolderAccess"); - FolderAccess access; - - if (!Enum.TryParse (value, out access)) - FolderAccess = FolderAccess.ReadOnly; - else - FolderAccess = access; - - FolderName = info.GetString ("FolderName"); - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The folder name. - /// The minimum folder access required by the operation. - /// The error message. - /// The inner exception. - /// - /// is null. - /// - public FolderNotOpenException (string folderName, FolderAccess access, string message, Exception innerException) : base (message, innerException) - { - if (folderName == null) - throw new ArgumentNullException (nameof (folderName)); - - FolderName = folderName; - FolderAccess = access; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The folder name. - /// The minimum folder access required by the operation. - /// The error message. - /// - /// is null. - /// - public FolderNotOpenException (string folderName, FolderAccess access, string message) : base (message) - { - if (folderName == null) - throw new ArgumentNullException (nameof (folderName)); - - FolderName = folderName; - FolderAccess = access; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The folder name. - /// The minimum folder access required by the operation. - /// - /// is null. - /// - public FolderNotOpenException (string folderName, FolderAccess access) : this (folderName, access, GetDefaultMessage (access)) - { - } - - /// - /// Get the name of the folder. - /// - /// - /// Gets the name of the folder. - /// - /// The name of the folder. - public string FolderName { - get; private set; - } - - /// - /// Get the minimum folder access required by the operation. - /// - /// - /// Gets the minimum folder access required by the operation. - /// - /// The minimum required folder access. - public FolderAccess FolderAccess { - get; private set; - } - - static string GetDefaultMessage (FolderAccess access) - { - if (access == FolderAccess.ReadWrite) - return "The folder is not currently open in read-write mode."; - - return "The folder is not currently open."; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Serializes the state of the . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("FolderAccess", FolderAccess.ToString ()); - info.AddValue ("FolderName", FolderName); - } -#endif - } -} diff --git a/src/MailKit/FolderQuota.cs b/src/MailKit/FolderQuota.cs deleted file mode 100644 index 056fe57..0000000 --- a/src/MailKit/FolderQuota.cs +++ /dev/null @@ -1,124 +0,0 @@ -// -// FolderQuota.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// A folder quota. - /// - /// - /// A is returned by . - /// - /// - /// - /// - public class FolderQuota - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new with the specified root. - /// - /// The quota root. - public FolderQuota (IMailFolder quotaRoot) - { - QuotaRoot = quotaRoot; - } - - /// - /// Get the quota root. - /// - /// - /// Gets the quota root. If the quota root is null, then - /// it suggests that the folder does not have a quota. - /// - /// - /// - /// - /// The quota root. - public IMailFolder QuotaRoot { - get; private set; - } - - /// - /// Get or set the message limit. - /// - /// - /// Gets or sets the message limit. - /// - /// - /// - /// - /// The message limit. - public uint? MessageLimit { - get; set; - } - - /// - /// Get or set the storage limit, in kilobytes. - /// - /// - /// Gets or sets the storage limit, in kilobytes. - /// - /// - /// - /// - /// The storage limit, in kilobytes. - public uint? StorageLimit { - get; set; - } - - /// - /// Get or set the current message count. - /// - /// - /// Gets or sets the current message count. - /// - /// - /// - /// - /// The current message count. - public uint? CurrentMessageCount { - get; set; - } - - /// - /// Gets or sets the size of the current storage, in kilobytes. - /// - /// - /// Gets or sets the size of the current storage, in kilobytes. - /// - /// - /// - /// - /// The size of the current storage, in kilobytes. - public uint? CurrentStorageSize { - get; set; - } - } -} diff --git a/src/MailKit/FolderRenamedEventArgs.cs b/src/MailKit/FolderRenamedEventArgs.cs deleted file mode 100644 index e83b6a1..0000000 --- a/src/MailKit/FolderRenamedEventArgs.cs +++ /dev/null @@ -1,85 +0,0 @@ -// -// FolderRenamedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Event args used when a is renamed. - /// - /// - /// Event args used when a is renamed. - /// - public class FolderRenamedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The old name of the folder. - /// The new name of the folder. - /// - /// is null. - /// -or- - /// is null. - /// - public FolderRenamedEventArgs (string oldName, string newName) - { - if (oldName == null) - throw new ArgumentNullException (nameof (oldName)); - - if (newName == null) - throw new ArgumentNullException (nameof (newName)); - - OldName = oldName; - NewName = newName; - } - - /// - /// The old name of the folder. - /// - /// - /// The old name of the folder. - /// - /// The old name. - public string OldName { - get; private set; - } - - /// - /// The new name of the folder. - /// - /// - /// The new name of the folder. - /// - /// The new name. - public string NewName { - get; private set; - } - } -} diff --git a/src/MailKit/IMailFolder.cs b/src/MailKit/IMailFolder.cs deleted file mode 100644 index b0065c7..0000000 --- a/src/MailKit/IMailFolder.cs +++ /dev/null @@ -1,5077 +0,0 @@ -// -// IMailFolder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; -using MailKit.Search; - -namespace MailKit { - /// - /// An interface for a mailbox folder as used by . - /// - /// - /// Implemented by message stores such as - /// - public interface IMailFolder : IEnumerable - { - /// - /// Gets an object that can be used to synchronize access to the folder. - /// - /// - /// Gets an object that can be used to synchronize access to the folder. - /// - /// The sync root. - object SyncRoot { get; } - - /// - /// Get the parent folder. - /// - /// - /// Root-level folders do not have a parent folder. - /// - /// The parent folder. - IMailFolder ParentFolder { get; } - - /// - /// Get the folder attributes. - /// - /// - /// Gets the folder attributes. - /// - /// The folder attributes. - FolderAttributes Attributes { get; } - - /// - /// Get the annotation access level. - /// - /// - /// If annotations are supported, this property can be used to determine whether or not - /// the supports reading and writing annotations. - /// - AnnotationAccess AnnotationAccess { get; } - - /// - /// Get the supported annotation scopes. - /// - /// - /// If annotations are supported, this property can be used to determine which - /// annotation scopes are supported by the . - /// - AnnotationScope AnnotationScopes { get; } - - /// - /// Get the maximum size of annotation values supported by the folder. - /// - /// - /// If annotations are supported, this property can be used to determine the - /// maximum size of annotation values supported by the . - /// - uint MaxAnnotationSize { get; } - - /// - /// Get the permanent flags. - /// - /// - /// The permanent flags are the message flags that will persist between sessions. - /// If the flag is set, then the folder allows - /// storing of user-defined (custom) message flags. - /// - /// The permanent flags. - MessageFlags PermanentFlags { get; } - - /// - /// Get the accepted flags. - /// - /// - /// The accepted flags are the message flags that will be accepted and persist - /// for the current session. For the set of flags that will persist between - /// sessions, see the property. - /// - /// The accepted flags. - MessageFlags AcceptedFlags { get; } - - /// - /// Get the directory separator. - /// - /// - /// Gets the directory separator. - /// - /// The directory separator. - char DirectorySeparator { get; } - - /// - /// Get the read/write access of the folder. - /// - /// - /// Gets the read/write access of the folder. - /// - /// The read/write access. - FolderAccess Access { get; } - - /// - /// Get whether or not the folder is a namespace folder. - /// - /// - /// Gets whether or not the folder is a namespace folder. - /// - /// true if the folder is a namespace folder; otherwise, false. - bool IsNamespace { get; } - - /// - /// Get the full name of the folder. - /// - /// - /// This is the equivalent of the full path of a file on a file system. - /// - /// The full name of the folder. - string FullName { get; } - - /// - /// Get the name of the folder. - /// - /// - /// This is the equivalent of the file name of a file on the file system. - /// - /// The name of the folder. - string Name { get; } - - /// - /// Get the unique identifier for the folder, if available. - /// - /// - /// Gets a unique identifier for the folder, if available. This is useful for clients - /// implementing a message cache that want to track the folder after it is renamed by another - /// client. - /// This property will only be available if the server supports the - /// OBJECTID extension. - /// - /// The unique folder identifier. - string Id { get; } - - /// - /// Get whether or not the folder is subscribed. - /// - /// - /// Gets whether or not the folder is subscribed. - /// - /// true if the folder is subscribed; otherwise, false. - bool IsSubscribed { get; } - - /// - /// Get whether or not the folder is currently open. - /// - /// - /// Gets whether or not the folder is currently open. - /// - /// true if the folder is currently open; otherwise, false. - bool IsOpen { get; } - - /// - /// Get whether or not the folder exists. - /// - /// - /// Gets whether or not the folder exists. - /// - /// true if the folder exists; otherwise, false. - bool Exists { get; } - - /// - /// Get whether or not the folder supports mod-sequences. - /// - /// - /// Gets whether or not the folder supports mod-sequences. - /// If mod-sequences are not supported by the folder, then all of the APIs that take a modseq - /// argument will throw and should not be used. - /// - /// true if the folder supports mod-sequences; otherwise, false. - [Obsolete ("Use Supports(FolderFeature.ModSequences) instead.")] - bool SupportsModSeq { get; } - - /// - /// Get the highest mod-sequence value of all messages in the mailbox. - /// - /// - /// Gets the highest mod-sequence value of all messages in the mailbox. - /// - /// The highest mod-sequence value. - ulong HighestModSeq { get; } - - /// - /// Get the Unique ID validity. - /// - /// - /// UIDs are only valid so long as the UID validity value remains unchanged. If and when - /// the folder's is changed, a client MUST discard its cache of UIDs - /// along with any summary information that it may have and re-query the folder. - /// The will only be set after the folder has been opened. - /// - /// The UID validity. - uint UidValidity { get; } - - /// - /// Get the UID that the next message that is added to the folder will be assigned. - /// - /// - /// This value will only be set after the folder has been opened. - /// - /// The next UID. - UniqueId? UidNext { get; } - - /// - /// Get the maximum size of a message that can be appended to the folder. - /// - /// - /// Gets the maximum size of a message that can be appended to the folder. - /// If the value is not set, then the limit is unspecified. - /// - /// The append limit. - uint? AppendLimit { get; } - - /// - /// Get the size of the folder. - /// - /// - /// Gets the size of the folder in bytes. - /// If the value is not set, then the size is unspecified. - /// - /// The size. - ulong? Size { get; } - - /// - /// Get the index of the first unread message in the folder. - /// - /// - /// This value will only be set after the folder has been opened. - /// - /// The index of the first unread message. - int FirstUnread { get; } - - /// - /// Get the number of unread messages in the folder. - /// - /// - /// Gets the number of unread messages in the folder. - /// This value will only be set after calling - /// - /// with . - /// - /// The number of unread messages. - int Unread { get; } - - /// - /// Get the number of recently delivered messages in the folder. - /// - /// - /// Gets the number of recently delivered messages in the folder. - /// - /// This value will only be set after calling - /// - /// with . - /// - /// The number of recently delivered messages. - int Recent { get; } - - /// - /// Get the total number of messages in the folder. - /// - /// - /// Gets the total number of messages in the folder. - /// - /// The total number of messages. - int Count { get; } - - /// - /// Get the threading algorithms supported by the folder. - /// - /// - /// Get the threading algorithms supported by the folder. - /// - /// The supported threading algorithms. - HashSet ThreadingAlgorithms { get; } - - /// - /// Determine whether or not an supports a feature. - /// - /// - /// Determines whether or not an supports a feature. - /// - /// The desired feature. - /// true if the feature is supported; otherwise, false. - bool Supports (FolderFeature feature); - - /// - /// Opens the folder using the requested folder access. - /// - /// - /// This variant of the - /// method is meant for quick resynchronization of the folder. Before calling this method, - /// the method MUST be called. - /// You should also make sure to add listeners to the and - /// events to get notifications of changes since - /// the last time the folder was opened. - /// - /// The state of the folder. - /// The requested folder access. - /// The last known value. - /// The last known value. - /// The last known list of unique message identifiers. - /// The cancellation token. - FolderAccess Open (FolderAccess access, uint uidValidity, ulong highestModSeq, IList uids, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously opens the folder using the requested folder access. - /// - /// - /// This variant of the - /// method is meant for quick resynchronization of the folder. Before calling this method, - /// the method MUST be called. - /// You should also make sure to add listeners to the and - /// events to get notifications of changes since - /// the last time the folder was opened. - /// - /// The state of the folder. - /// The requested folder access. - /// The last known value. - /// The last known value. - /// The last known list of unique message identifiers. - /// The cancellation token. - Task OpenAsync (FolderAccess access, uint uidValidity, ulong highestModSeq, IList uids, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Open the folder using the requested folder access. - /// - /// - /// Opens the folder using the requested folder access. - /// - /// The state of the folder. - /// The requested folder access. - /// The cancellation token. - FolderAccess Open (FolderAccess access, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously open the folder using the requested folder access. - /// - /// - /// Asynchronously opens the folder using the requested folder access. - /// - /// The state of the folder. - /// The requested folder access. - /// The cancellation token. - Task OpenAsync (FolderAccess access, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Close the folder, optionally expunging the messages marked for deletion. - /// - /// - /// Closes the folder, optionally expunging the messages marked for deletion. - /// - /// If set to true, expunge. - /// The cancellation token. - void Close (bool expunge = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously close the folder, optionally expunging the messages marked for deletion. - /// - /// - /// Asynchronously closes the folder, optionally expunging the messages marked for deletion. - /// - /// An asynchronous task context. - /// If set to true, expunge. - /// The cancellation token. - Task CloseAsync (bool expunge = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// true if the folder will be used to contain messages; otherwise false. - /// The cancellation token. - IMailFolder Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously create a new subfolder with the given name. - /// - /// - /// Asynchronously creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// true if the folder will be used to contain messages; otherwise false. - /// The cancellation token. - Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// A list of special uses for the folder being created. - /// The cancellation token. - IMailFolder Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously create a new subfolder with the given name. - /// - /// - /// Asynchronously creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// A list of special uses for the folder being created. - /// The cancellation token. - Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// The special use for the folder being created. - /// The cancellation token. - IMailFolder Create (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously create a new subfolder with the given name. - /// - /// - /// Asynchronously creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// The special use for the folder being created. - /// The cancellation token. - Task CreateAsync (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Rename the folder. - /// - /// - /// Renames the folder. - /// - /// The new parent folder. - /// The new name of the folder. - /// The cancellation token. - void Rename (IMailFolder parent, string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously rename the folder. - /// - /// - /// Asynchronously renames the folder. - /// - /// An asynchronous task context. - /// The new parent folder. - /// The new name of the folder. - /// The cancellation token. - Task RenameAsync (IMailFolder parent, string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Delete the folder. - /// - /// - /// Deletes the folder. - /// - /// The cancellation token. - void Delete (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously delete the folder. - /// - /// - /// Asynchronously deletes the folder. - /// - /// An asynchronous task context. - /// The cancellation token. - Task DeleteAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Subscribe to the folder. - /// - /// - /// Subscribes to the folder. - /// - /// The cancellation token. - void Subscribe (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously subscribe to the folder. - /// - /// - /// Asynchronously subscribes to the folder. - /// - /// An asynchronous task context. - /// The cancellation token. - Task SubscribeAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Unsubscribe from the folder. - /// - /// - /// Unsubscribes from the folder. - /// - /// The cancellation token. - void Unsubscribe (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously unsubscribe from the folder. - /// - /// - /// Asynchronously unsubscribes from the folder. - /// - /// An asynchronous task context. - /// The cancellation token. - Task UnsubscribeAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the subfolders. - /// - /// - /// Gets the subfolders as well as queries the server for the status of the requested items. - /// When the argument is non-empty, this has the equivalent functionality - /// of calling and then calling - /// on each of the returned folders. - /// Using this method is potentially more efficient than querying the status of each returned folder. - /// - /// The subfolders. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - IList GetSubfolders (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the subfolders. - /// - /// - /// Asynchronously gets the subfolders as well as queries the server for the status of the requested items. - /// When the argument is non-empty, this has the equivalent functionality - /// of calling and then calling - /// on each of the returned folders. - /// - /// The subfolders. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - Task> GetSubfoldersAsync (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the subfolders. - /// - /// - /// Gets the subfolders. - /// - /// The subfolders. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - IList GetSubfolders (bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the subfolders. - /// - /// - /// Asynchronously gets the subfolders. - /// - /// The subfolders. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - Task> GetSubfoldersAsync (bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the specified subfolder. - /// - /// - /// Gets the specified subfolder. - /// - /// The subfolder. - /// The name of the subfolder. - /// The cancellation token. - IMailFolder GetSubfolder (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the specified subfolder. - /// - /// - /// Asynchronously gets the specified subfolder. - /// - /// The subfolder. - /// The name of the subfolder. - /// The cancellation token. - Task GetSubfolderAsync (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Force the server to flush its state for the folder. - /// - /// - /// Forces the server to flush its state for the folder. - /// - /// The cancellation token. - void Check (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously force the server to flush its state for the folder. - /// - /// - /// Asynchronously forces the server to flush its state for the folder. - /// - /// An asynchronous task context. - /// The cancellation token. - Task CheckAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Update the values of the specified items. - /// - /// - /// Updates the values of the specified items. - /// The method - /// MUST NOT be used on a folder that is already in the opened state. Instead, other ways - /// of getting the desired information should be used. - /// For example, a common use for the - /// method is to get the number of unread messages in the folder. When the folder is open, however, it is - /// possible to use the - /// method to query for the list of unread messages. - /// - /// The items to update. - /// The cancellation token. - void Status (StatusItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously update the values of the specified items. - /// - /// - /// Updates the values of the specified items. - /// The method - /// MUST NOT be used on a folder that is already in the opened state. Instead, other ways - /// of getting the desired information should be used. - /// For example, a common use for the - /// method is to get the number of unread messages in the folder. When the folder is open, however, it is - /// possible to use the - /// method to query for the list of unread messages. - /// - /// An asynchronous task context. - /// The items to update. - /// The cancellation token. - Task StatusAsync (StatusItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the complete access control list for the folder. - /// - /// - /// Gets the complete access control list for the folder. - /// - /// The access control list. - /// The cancellation token. - AccessControlList GetAccessControlList (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the complete access control list for the folder. - /// - /// - /// Asynchronously gets the complete access control list for the folder. - /// - /// The access control list. - /// The cancellation token. - Task GetAccessControlListAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the access rights for a particular identifier. - /// - /// - /// Gets the access rights for a particular identifier. - /// - /// The access rights. - /// The identifier name. - /// The cancellation token. - AccessRights GetAccessRights (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the access rights for a particular identifier. - /// - /// - /// Asynchronously gets the access rights for a particular identifier. - /// - /// The access rights. - /// The identifier name. - /// The cancellation token. - Task GetAccessRightsAsync (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the access rights for the current authenticated user. - /// - /// - /// Gets the access rights for the current authenticated user. - /// - /// The access rights. - /// The cancellation token. - AccessRights GetMyAccessRights (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the access rights for the current authenticated user. - /// - /// - /// Asynchronously gets the access rights for the current authenticated user. - /// - /// The access rights. - /// The cancellation token. - Task GetMyAccessRightsAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add access rights for the specified identity. - /// - /// - /// Adds the given access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - void AddAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add access rights for the specified identity. - /// - /// - /// Asynchronously adds the given access rights for the specified identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The access rights. - /// The cancellation token. - Task AddAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove access rights for the specified identity. - /// - /// - /// Removes the given access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - void RemoveAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove access rights for the specified identity. - /// - /// - /// Asynchronously removes the given access rights for the specified identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The access rights. - /// The cancellation token. - Task RemoveAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the access rights for the specified identity. - /// - /// - /// Sets the access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - void SetAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the access rights for the sepcified identity. - /// - /// - /// Asynchronously sets the access rights for the specified identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The access rights. - /// The cancellation token. - Task SetAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove all access rights for the given identity. - /// - /// - /// Removes all access rights for the given identity. - /// - /// The identity name. - /// The cancellation token. - void RemoveAccess (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove all access rights for the given identity. - /// - /// - /// Asynchronously removes all access rights for the given identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The cancellation token. - Task RemoveAccessAsync (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the quota information for the folder. - /// - /// - /// Gets the quota information for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The folder quota. - /// The cancellation token. - FolderQuota GetQuota (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the quota information for the folder. - /// - /// - /// Asynchronously gets the quota information for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The folder quota. - /// The cancellation token. - Task GetQuotaAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the quota limits for the folder. - /// - /// - /// Sets the quota limits for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The updated folder quota. - /// If not null, sets the maximum number of messages to allow. - /// If not null, sets the maximum storage size (in kilobytes). - /// The cancellation token. - FolderQuota SetQuota (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the quota limits for the folder. - /// - /// - /// Asynchronously sets the quota limits for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The updated folder quota. - /// If not null, sets the maximum number of messages to allow. - /// If not null, sets the maximum storage size (in kilobytes). - /// The cancellation token. - Task SetQuotaAsync (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata tags. - /// The cancellation token. - MetadataCollection GetMetadata (IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata. - /// The metadata tags. - /// The cancellation token. - Task GetMetadataAsync (IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - MetadataCollection GetMetadata (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - Task GetMetadataAsync (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sets the specified metadata. - /// - /// - /// Sets the specified metadata. - /// - /// The metadata. - /// The cancellation token. - void SetMetadata (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sets the specified metadata. - /// - /// - /// Asynchronously sets the specified metadata. - /// - /// An asynchronous task context. - /// The metadata. - /// The cancellation token. - Task SetMetadataAsync (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Expunge the folder, permanently removing all messages marked for deletion. - /// - /// - /// Expunges the folder, permanently removing all messages marked for deletion. - /// Normally, an event will be emitted for each - /// message that is expunged. However, if the mail store supports the quick - /// resynchronization feature and it has been enabled via the - /// method, then - /// the event will be emitted rather than the - /// event. - /// - /// The cancellation token. - void Expunge (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously expunge the folder, permanently removing all messages marked for deletion. - /// - /// - /// Asynchronously expunges the folder, permanently removing all messages marked for deletion. - /// Normally, an event will be emitted for - /// each message that is expunged. However, if the mail store supports the quick - /// resynchronization feature and it has been enabled via the - /// method, then - /// the event will be emitted rather than the - /// event. - /// - /// An asynchronous task context. - /// The cancellation token. - Task ExpungeAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Expunge the specified uids, permanently removing them from the folder. - /// - /// - /// Expunges the specified uids, permanently removing them from the folder. - /// Normally, an event will be emitted for - /// each message that is expunged. However, if the mail store supports the quick - /// resynchronization feature and it has been enabled via the - /// method, then - /// the event will be emitted rather than the - /// event. - /// - /// The message uids. - /// The cancellation token. - void Expunge (IList uids, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously expunge the specified uids, permanently removing them from the folder. - /// - /// - /// Asynchronously expunges the specified uids, permanently removing them from the folder. - /// Normally, an event will be emitted for - /// each message that is expunged. However, if the mail store supports the quick - /// resynchronization feature and it has been enabled via the - /// method, then - /// the event will be emitted rather than the - /// event. - /// - /// An asynchronous task context. - /// The message uids. - /// The cancellation token. - Task ExpungeAsync (IList uids, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Append (MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - Task AppendAsync (MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Append (MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task AppendAsync (MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Append (MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - Task AppendAsync (MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The list of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - IList Append (IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Asynchronously appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The list of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - Task> AppendAsync (IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The list of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - IList Append (IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Asynchronously appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The list of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - Task> AppendAsync (IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The list of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - IList Append (FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Asynchronously appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The list of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - Task> AppendAsync (FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The list of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - IList Append (FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Asynchronously appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The list of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - Task> AppendAsync (FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Replace (UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - Task ReplaceAsync (UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Replace (UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task ReplaceAsync (UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Replace (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - Task ReplaceAsync (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Replace (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task ReplaceAsync (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Replace (int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism.; - Task ReplaceAsync (int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Replace (int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task ReplaceAsync (int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Replace (FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - Task ReplaceAsync (FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - UniqueId? Replace (FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task ReplaceAsync (FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Copy the specified message to the destination folder. - /// - /// - /// Copies the specified message to the destination folder. - /// - /// The UID of the message in the destination folder, if available; otherwise, null. - /// The UID of the message to copy. - /// The destination folder. - /// The cancellation token. - UniqueId? CopyTo (UniqueId uid, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously copy the specified message to the destination folder. - /// - /// - /// Asynchronously copies the specified message to the destination folder. - /// - /// The UID of the message in the destination folder, if available; otherwise, null. - /// The UID of the message to copy. - /// The destination folder. - /// The cancellation token. - Task CopyToAsync (UniqueId uid, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Copy the specified messages to the destination folder. - /// - /// - /// Copies the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to copy. - /// The destination folder. - /// The cancellation token. - UniqueIdMap CopyTo (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously copy the specified messages to the destination folder. - /// - /// - /// Asynchronously copies the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to copy. - /// The destination folder. - /// The cancellation token. - Task CopyToAsync (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Move the specified message to the destination folder. - /// - /// - /// Moves the specified message to the destination folder. - /// - /// The UID of the message in the destination folder, if available; otherwise, null. - /// The UID of the message to move. - /// The destination folder. - /// The cancellation token. - UniqueId? MoveTo (UniqueId uid, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously move the specified message to the destination folder. - /// - /// - /// Asynchronously moves the specified message to the destination folder. - /// - /// The UID of the message in the destination folder, if available; otherwise, null. - /// The UID of the message to move. - /// The destination folder. - /// The cancellation token. - Task MoveToAsync (UniqueId uid, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Move the specified messages to the destination folder. - /// - /// - /// Moves the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to copy. - /// The destination folder. - /// The cancellation token. - UniqueIdMap MoveTo (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously move the specified messages to the destination folder. - /// - /// - /// Asynchronously moves the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to copy. - /// The destination folder. - /// The cancellation token. - Task MoveToAsync (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Copy the specified message to the destination folder. - /// - /// - /// Copies the specified message to the destination folder. - /// - /// The index of the message to copy. - /// The destination folder. - /// The cancellation token. - void CopyTo (int index, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously copy the specified message to the destination folder. - /// - /// - /// Asynchronously copies the specified message to the destination folder. - /// - /// An asynchronous task context. - /// The indexes of the message to copy. - /// The destination folder. - /// The cancellation token. - Task CopyToAsync (int index, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Copy the specified messages to the destination folder. - /// - /// - /// Copies the specified messages to the destination folder. - /// - /// The indexes of the messages to copy. - /// The destination folder. - /// The cancellation token. - void CopyTo (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously copy the specified messages to the destination folder. - /// - /// - /// Asynchronously copies the specified messages to the destination folder. - /// - /// An asynchronous task context. - /// The indexes of the messages to copy. - /// The destination folder. - /// The cancellation token. - Task CopyToAsync (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Move the specified message to the destination folder. - /// - /// - /// Moves the specified message to the destination folder. - /// - /// The index of the message to move. - /// The destination folder. - /// The cancellation token. - void MoveTo (int index, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously move the specified message to the destination folder. - /// - /// - /// Asynchronously moves the specified message to the destination folder. - /// - /// An asynchronous task context. - /// The index of the message to move. - /// The destination folder. - /// The cancellation token. - Task MoveToAsync (int index, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Move the specified messages to the destination folder. - /// - /// - /// Moves the specified messages to the destination folder. - /// - /// The indexes of the messages to move. - /// The destination folder. - /// The cancellation token. - void MoveTo (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously move the specified messages to the destination folder. - /// - /// - /// Asynchronously moves the specified messages to the destination folder. - /// - /// An asynchronous task context. - /// The indexes of the messages to move. - /// The destination folder. - /// The cancellation token. - Task MoveToAsync (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// - /// - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The cancellation token. - IList Fetch (IList uids, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The cancellation token. - Task> FetchAsync (IList uids, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The cancellation token. - IList Fetch (IList indexes, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The cancellation token. - Task> FetchAsync (IList indexes, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes that have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes that have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes that have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The cancellation token. - IList Fetch (int min, int max, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The cancellation token. - Task> FetchAsync (int min, int max, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes - /// (inclusive) that have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes (inclusive) that have a higher mod-sequence value than the - /// one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes - /// (inclusive) that have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes (inclusive) that have a higher mod-sequence value than the - /// one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes - /// (inclusive) that have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes (inclusive) that have a higher mod-sequence value than the - /// one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the specified message headers. - /// - /// - /// Gets the specified message headers. - /// - /// The message headers. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified message headers. - /// - /// - /// Asynchronously gets the specified message headers. - /// - /// The message headers. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Asynchronously gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified message headers. - /// - /// - /// Gets the specified message headers. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - HeaderList GetHeaders (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified message headers. - /// - /// - /// Asynchronously gets the specified message headers. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetHeadersAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Asynchronously gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified message. - /// - /// - /// Gets the specified message. - /// - /// The message. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified message. - /// - /// - /// Asynchronously gets the specified message. - /// - /// The message. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified message. - /// - /// - /// Gets the specified message. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - MimeMessage GetMessage (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified message. - /// - /// - /// Asynchronously gets the specified message. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// - /// - /// - /// The body part. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Asynchronously gets the specified body part. - /// - /// The body part. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Asynchronously gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified body part. - /// - /// - /// Gets a substream of the body part. If the starting offset is beyond - /// the end of the body part, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the body part, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The desired body part. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified body part. - /// - /// - /// Asynchronously gets a substream of the body part. If the starting offset is beyond - /// the end of the body part, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the body part, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The desired body part. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified body part. - /// - /// - /// Gets a substream of the body part. If the starting offset is beyond - /// the end of the body part, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the body part, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The desired body part. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified body part. - /// - /// - /// Asynchronously gets a substream of the body part. If the starting offset is beyond - /// the end of the body part, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the body part, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The desired body part. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. If the starting offset is beyond - /// the end of the specified section of the message, an empty stream is returned. If - /// the number of bytes desired extends beyond the end of the section, a truncated - /// stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the specified message. If the starting - /// offset is beyond the end of the specified section of the message, an empty stream - /// is returned. If the number of bytes desired extends beyond the end of the section, - /// a truncated stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (int index, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. If the starting offset is beyond - /// the end of the specified section of the message, an empty stream is returned. If - /// the number of bytes desired extends beyond the end of the section, a truncated - /// stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the specified message. If the starting - /// offset is beyond the end of the specified section of the message, an empty stream - /// is returned. If the number of bytes desired extends beyond the end of the section, - /// a truncated stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Add a set of flags to the specified message. - /// - /// - /// Adds a set of flags to the specified message. - /// - /// The UID of the message. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddFlags (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified message. - /// - /// - /// Asynchronously adds a set of flags to the specified message. - /// - /// An asynchronous task context. - /// The UIDs of the message. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddFlagsAsync (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified message. - /// - /// - /// Adds a set of flags to the specified message. - /// - /// The UID of the message. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddFlags (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified message. - /// - /// - /// Asynchronously adds a set of flags to the specified message. - /// - /// An asynchronous task context. - /// The UIDs of the message. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddFlagsAsync (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddFlags (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages. - /// - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddFlagsAsync (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages. - /// - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified message. - /// - /// - /// Removes a set of flags from the specified message. - /// - /// The UID of the message. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveFlags (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified message. - /// - /// - /// Asynchronously removes a set of flags from the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveFlagsAsync (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified message. - /// - /// - /// Removes a set of flags from the specified message. - /// - /// The UID of the message. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveFlags (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified message. - /// - /// - /// Asynchronously removes a set of flags from the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveFlagsAsync (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveFlags (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages. - /// - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveFlagsAsync (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages. - /// - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified message. - /// - /// - /// Sets the flags of the specified message. - /// - /// The UID of the message. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetFlags (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified message. - /// - /// - /// Asynchronously sets the flags of the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetFlagsAsync (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified message. - /// - /// - /// Sets the flags of the specified message. - /// - /// The UID of the message. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetFlags (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified message. - /// - /// - /// Asynchronously sets the flags of the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetFlagsAsync (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetFlags (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages. - /// - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetFlagsAsync (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages. - /// - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList AddFlags (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> AddFlagsAsync (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList AddFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> AddFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList RemoveFlags (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> RemoveFlagsAsync (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList RemoveFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> RemoveFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList SetFlags (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> SetFlagsAsync (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList SetFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> SetFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified message. - /// - /// - /// Adds a set of flags to the specified message. - /// - /// The index of the message. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddFlags (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified message. - /// - /// - /// Asynchronously adds a set of flags to the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddFlagsAsync (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified message. - /// - /// - /// Adds a set of flags to the specified message. - /// - /// The index of the message. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddFlags (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified message. - /// - /// - /// Asynchronously adds a set of flags to the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddFlagsAsync (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The indexes of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddFlags (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages. - /// - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddFlagsAsync (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The indexes of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages. - /// - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified message. - /// - /// - /// Removes a set of flags from the specified message. - /// - /// The index of the message. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveFlags (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified message. - /// - /// - /// Asynchronously removes a set of flags from the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveFlagsAsync (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified message. - /// - /// - /// Removes a set of flags from the specified message. - /// - /// The index of the message. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveFlags (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified message. - /// - /// - /// Asynchronously removes a set of flags from the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveFlagsAsync (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The indexes of the messages. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveFlags (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages. - /// - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveFlagsAsync (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The indexes of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages. - /// - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified message. - /// - /// - /// Sets the flags of the specified message. - /// - /// The index of the message. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetFlags (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified message. - /// - /// - /// Asynchronously sets the flags of the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetFlagsAsync (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified message. - /// - /// - /// Sets the flags of the specified message. - /// - /// The index of the message. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetFlags (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified message. - /// - /// - /// Asynchronously sets the flags of the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetFlagsAsync (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The indexes of the messages. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetFlags (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages. - /// - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetFlagsAsync (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The indexes of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages. - /// - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList AddFlags (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> AddFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList AddFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> AddFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList RemoveFlags (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> RemoveFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList RemoveFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> RemoveFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList SetFlags (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> SetFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList SetFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> SetFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified message. - /// - /// - /// Adds a set of labels to the specified message. - /// - /// The UID of the message. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddLabels (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified message. - /// - /// - /// Asynchronously adds a set of labels to the specified message. - /// - /// An asynchronous task context. - /// The UIDs of the message. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddLabelsAsync (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified messages. - /// - /// - /// Adds a set of labels to the specified messages. - /// - /// The UIDs of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified messages. - /// - /// - /// Asynchronously adds a set of labels to the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified message. - /// - /// - /// Removes a set of labels from the specified message. - /// - /// The UID of the message. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveLabels (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified message. - /// - /// - /// Asynchronously removes a set of labels from the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveLabelsAsync (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified messages. - /// - /// - /// Removes a set of labels from the specified messages. - /// - /// The UIDs of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified messages. - /// - /// - /// Asynchronously removes a set of labels from the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified message. - /// - /// - /// Sets the labels of the specified message. - /// - /// The UID of the message. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetLabels (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified message. - /// - /// - /// Asynchronously sets the labels of the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetLabelsAsync (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified messages. - /// - /// - /// Sets the labels of the specified messages. - /// - /// The UIDs of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified messages. - /// - /// - /// Asynchronously sets the labels of the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList AddLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> AddLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList RemoveLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> RemoveLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList SetLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> SetLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified message. - /// - /// - /// Adds a set of labels to the specified message. - /// - /// The index of the message. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddLabels (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified message. - /// - /// - /// Asynchronously adds a set of labels to the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddLabelsAsync (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified messages. - /// - /// - /// Adds a set of labels to the specified messages. - /// - /// The indexes of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - void AddLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified messages. - /// - /// - /// Asynchronously adds a set of labels to the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task AddLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified message. - /// - /// - /// Removes a set of labels from the specified message. - /// - /// The index of the message. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveLabels (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified message. - /// - /// - /// Asynchronously removes a set of labels from the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveLabelsAsync (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified messages. - /// - /// - /// Removes a set of labels from the specified messages. - /// - /// The indexes of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - void RemoveLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified messages. - /// - /// - /// Asynchronously removes a set of labels from the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task RemoveLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified message. - /// - /// - /// Sets the labels of the specified message. - /// - /// The index of the message. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetLabels (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified message. - /// - /// - /// Asynchronously sets the labels of the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetLabelsAsync (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified messages. - /// - /// - /// Sets the labels of the specified messages. - /// - /// The indexes of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - void SetLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified messages. - /// - /// - /// Asynchronously sets the labels of the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task SetLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList AddLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> AddLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList RemoveLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> RemoveLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - IList SetLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - Task> SetLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified message. - /// - /// - /// Stores the annotations for the specified message. - /// - /// The UID of the message. - /// The annotations to store. - /// The cancellation token. - void Store (UniqueId uid, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified message. - /// - /// - /// Asynchronously stores the annotations for the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The annotations to store. - /// The cancellation token. - Task StoreAsync (UniqueId uid, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified messages. - /// - /// - /// Stores the annotations for the specified messages. - /// - /// The UIDs of the messages. - /// The annotations to store. - /// The cancellation token. - void Store (IList uids, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified messages. - /// - /// - /// Asynchronously stores the annotations for the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The annotations to store. - /// The cancellation token. - Task StoreAsync (IList uids, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - IList Store (IList uids, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - Task> StoreAsync (IList uids, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified message. - /// - /// - /// Stores the annotations for the specified message. - /// - /// The index of the message. - /// The annotations to store. - /// The cancellation token. - void Store (int index, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified message. - /// - /// - /// Asynchronously stores the annotations for the specified message. - /// - /// An asynchronous task context. - /// The indexes of the message. - /// The annotations to store. - /// The cancellation token. - Task StoreAsync (int index, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified messages. - /// - /// - /// Stores the annotations for the specified messages. - /// - /// The indexes of the messages. - /// The annotations to store. - /// The cancellation token. - void Store (IList indexes, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified messages. - /// - /// - /// Asynchronously stores the annotations for the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The annotations to store. - /// The cancellation token. - Task StoreAsync (IList indexes, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - IList Store (IList indexes, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously stores the annotations for the specified messages only if their mod-sequence value is less than the specified value.s - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - Task> StoreAsync (IList indexes, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Search the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - IList Search (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously search the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - Task> SearchAsync (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Search the subset of UIDs in the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs. - /// The subset of UIDs - /// The search query. - /// The cancellation token. - IList Search (IList uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously search the subset of UIDs in the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs. - /// The subset of UIDs - /// The search query. - /// The cancellation token. - Task> SearchAsync (IList uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Search the folder for messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The search query. - /// The cancellation token. - SearchResults Search (SearchOptions options, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously search the folder for messages matching the specified query. - /// - /// - /// Asynchronously searches the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The search query. - /// The cancellation token. - Task SearchAsync (SearchOptions options, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Search the subset of UIDs in the folder for messages matching the specified query. - /// - /// - /// Searches the fsubset of UIDs in the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The subset of UIDs - /// The search query. - /// The cancellation token. - SearchResults Search (SearchOptions options, IList uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously search the subset of UIDs in the folder for messages matching the specified query. - /// - /// - /// Asynchronously searches the fsubset of UIDs in the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The subset of UIDs - /// The search query. - /// The cancellation token. - Task SearchAsync (SearchOptions options, IList uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The search query. - /// The sort order. - /// The cancellation token. - IList Sort (SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The search query. - /// The sort order. - /// The cancellation token. - Task> SortAsync (SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The subset of UIDs - /// The search query. - /// The sort order. - /// The cancellation token. - IList Sort (IList uids, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The subset of UIDs - /// The search query. - /// The sort order. - /// The cancellation token. - Task> SortAsync (IList uids, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sort messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The search query. - /// The sort order. - /// The cancellation token. - SearchResults Sort (SearchOptions options, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// Asynchronously searches the folder for messages matching the specified query, returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The search query. - /// The sort order. - /// The cancellation token. - Task SortAsync (SearchOptions options, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sort messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The subset of UIDs - /// The search query. - /// The sort order. - /// The cancellation token. - SearchResults Sort (SearchOptions options, IList uids, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// Asynchronously searches the folder for messages matching the specified query, - /// returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The subset of UIDs - /// The search query. - /// The sort order. - /// The cancellation token. - Task SortAsync (SearchOptions options, IList uids, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - IList Thread (ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - Task> ThreadAsync (ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The subset of UIDs - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - IList Thread (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The subset of UIDs - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - Task> ThreadAsync (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Occurs when the folder is opened. - /// - /// - /// Emitted when the folder is opened. - /// - event EventHandler Opened; - - /// - /// Occurs when the folder is closed. - /// - /// - /// Emitted when the folder is closed. - /// - event EventHandler Closed; - - /// - /// Occurs when the folder is deleted. - /// - /// - /// Emitted when the folder is deleted. - /// - event EventHandler Deleted; - - /// - /// Occurs when the folder is renamed. - /// - /// - /// Emitted when the folder is renamed. - /// - event EventHandler Renamed; - - /// - /// Occurs when the folder is subscribed. - /// - /// - /// Emitted when the folder is subscribed. - /// - event EventHandler Subscribed; - - /// - /// Occurs when the folder is unsubscribed. - /// - /// - /// Emitted when the folder is unsubscribed. - /// - event EventHandler Unsubscribed; - - /// - /// Occurs when a message is expunged from the folder. - /// - /// - /// Emitted when a message is expunged from the folder. - /// - /// - /// - /// - event EventHandler MessageExpunged; - - /// - /// Occurs when messages vanish from the folder. - /// - /// - /// Emitted when a messages vanish from the folder. - /// - event EventHandler MessagesVanished; - - /// - /// Occurs when flags changed on a message. - /// - /// - /// Emitted when flags changed on a message. - /// - /// - /// - /// - event EventHandler MessageFlagsChanged; - - /// - /// Occurs when labels changed on a message. - /// - /// - /// Emitted when labels changed on a message. - /// - event EventHandler MessageLabelsChanged; - - /// - /// Occurs when annotations changed on a message. - /// - /// - /// Emitted when annotations changed on a message. - /// - event EventHandler AnnotationsChanged; - - /// - /// Occurs when a message summary is fetched from the folder. - /// - /// - /// Emitted when a message summary is fetched from the folder. - /// When multiple message summaries are being fetched from a remote folder, - /// it is possible that the connection will drop or some other exception will - /// occur, causing the Fetch method to fail, requiring the client to request the - /// same set of message summaries again after it reconnects. This is obviously - /// inefficient. To alleviate this potential problem, this event will be emitted - /// as soon as the successfully retrieves the complete - /// for each requested message. - /// The Fetch - /// methods will return a list of all message summaries that any information was - /// retrieved for, regardless of whether or not all of the requested items were fetched, - /// therefore there may be a discrepency between the number of times this event is - /// emitetd and the number of summary items returned from the Fetch method. - /// - event EventHandler MessageSummaryFetched; - - /// - /// Occurs when metadata changes. - /// - /// - /// The event is emitted when metadata changes. - /// - event EventHandler MetadataChanged; - - /// - /// Occurs when the mod-sequence changed on a message. - /// - /// - /// Emitted when the mod-sequence changed on a message. - /// - event EventHandler ModSeqChanged; - - /// - /// Occurs when the highest mod-sequence changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - event EventHandler HighestModSeqChanged; - - /// - /// Occurs when the next UID changes. - /// - /// - /// Emitted when the property changes. - /// - event EventHandler UidNextChanged; - - /// - /// Occurs when the UID validity changes. - /// - /// - /// Emitted when the property changes. - /// - event EventHandler UidValidityChanged; - - /// - /// Occurs when the ID changes. - /// - /// - /// Emitted when the property changes. - /// - event EventHandler IdChanged; - - /// - /// Occurs when the size of the folder changes. - /// - /// - /// Emitted when the property changes. - /// - event EventHandler SizeChanged; - - /// - /// Occurs when the message count changes. - /// - /// - /// Emitted when the property changes. - /// - /// - /// - /// - event EventHandler CountChanged; - - /// - /// Occurs when the recent message count changes. - /// - /// - /// Emitted when the property changes. - /// - event EventHandler RecentChanged; - - /// - /// Occurs when the message unread count changes. - /// - /// - /// Emitted when the property changes. - /// - event EventHandler UnreadChanged; - } -} diff --git a/src/MailKit/IMailService.cs b/src/MailKit/IMailService.cs deleted file mode 100644 index d350f49..0000000 --- a/src/MailKit/IMailService.cs +++ /dev/null @@ -1,1118 +0,0 @@ -// -// IMailService.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Net.Security; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; -using SslProtocols = System.Security.Authentication.SslProtocols; - -using MailKit.Net.Proxy; - -using MailKit.Security; - -namespace MailKit { - /// - /// An interface for message services such as SMTP, POP3, or IMAP. - /// - /// - /// Implemented by - /// and . - /// - public interface IMailService : IDisposable - { - /// - /// Gets an object that can be used to synchronize access to the folder. - /// - /// - /// Gets an object that can be used to synchronize access to the folder. - /// - /// The sync root. - object SyncRoot { get; } - - /// - /// Gets or sets the SSL and TLS protocol versions that the client is allowed to use. - /// - /// - /// Gets or sets the SSL and TLS protocol versions that the client is allowed to use. - /// By default, MailKit initializes this value to support only TLS v1.0 and greater and - /// does not support any version of SSL due to those protocols no longer being considered - /// secure. - /// This property should be set before calling any of the - /// Connect methods. - /// - /// The SSL and TLS protocol versions that are supported. - SslProtocols SslProtocols { get; set; } - - /// - /// Get or set the client SSL certificates. - /// - /// - /// Some servers may require the client SSL certificates in order - /// to allow the user to connect. - /// This property should be set before calling any of the - /// Connect methods. - /// - /// The client SSL certificates. - X509CertificateCollection ClientCertificates { get; set; } - - /// - /// Get or set whether connecting via SSL/TLS should check certificate revocation. - /// - /// - /// Gets or sets whether connecting via SSL/TLS should check certificate revocation. - /// Normally, the value of this property should be set to true (the default) for security - /// reasons, but there are times when it may be necessary to set it to false. - /// For example, most Certificate Authorities are probably pretty good at keeping their CRL and/or - /// OCSP servers up 24/7, but occasionally they do go down or are otherwise unreachable due to other - /// network problems between the client and the Certificate Authority. When this happens, it becomes - /// impossible to check the revocation status of one or more of the certificates in the chain - /// resulting in an being thrown in the - /// Connect method. If this becomes a problem, - /// it may become desirable to set to false. - /// - /// true if certificate revocation should be checked; otherwise, false. - bool CheckCertificateRevocation { get; set; } - - /// - /// Get or sets a callback function to validate the server certificate. - /// - /// - /// Gets or sets a callback function to validate the server certificate. - /// This property should be set before calling any of the - /// Connect methods. - /// - /// - /// - /// - /// The server certificate validation callback function. - RemoteCertificateValidationCallback ServerCertificateValidationCallback { get; set; } - - /// - /// Get or set the local IP end point to use when connecting to a remote host. - /// - /// - /// Gets or sets the local IP end point to use when connecting to a remote host. - /// - /// The local IP end point or null to use the default end point. - IPEndPoint LocalEndPoint { get; set; } - - /// - /// Get or set the proxy client to use when connecting to a remote host. - /// - /// - /// Gets or sets the proxy client to use when connecting to a remote host via any of the - /// Connect methods. - /// - /// The proxy client. - IProxyClient ProxyClient { get; set; } - - /// - /// Get the authentication mechanisms supported by the message service. - /// - /// - /// The authentication mechanisms are queried durring the - /// Connect method. - /// - /// The supported authentication mechanisms. - HashSet AuthenticationMechanisms { get; } - - /// - /// Get whether or not the client is currently authenticated with the mail server. - /// - /// - /// Gets whether or not the client is currently authenticated with the mail server. - /// To authenticate with the mail server, use one of the - /// Authenticate methods - /// or any of the Async alternatives. - /// - /// true if the client is authenticated; otherwise, false. - bool IsAuthenticated { get; } - - /// - /// Get whether or not the service is currently connected. - /// - /// - /// The state is set to true immediately after - /// one of the Connect - /// methods succeeds and is not set back to false until either the client - /// is disconnected via or until a - /// is thrown while attempting to read or write to - /// the underlying network socket. - /// When an is caught, the connection state of the - /// should be checked before continuing. - /// - /// true if the service connected; otherwise, false. - bool IsConnected { get; } - - /// - /// 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). - /// - /// true if the connection is secure; otherwise, false. - bool IsSecure { get; } - - /// - /// Get or set the timeout for network streaming operations, in milliseconds. - /// - /// - /// Gets or sets the underlying socket stream's - /// and values. - /// - /// The timeout in milliseconds. - int Timeout { get; set; } - - /// - /// Establish a connection to the specified mail server. - /// - /// - /// Establish a connection to the specified mail server. - /// If a successful connection is made, the - /// property will be populated. - /// - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// true if the client should make an SSL-wrapped connection to the server; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - void Connect (string host, int port, bool useSsl, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously establish a connection to the specified mail server. - /// - /// - /// Asynchronously establishes a connection to the specified mail server. - /// If a successful connection is made, the - /// property will be populated. - /// - /// An asynchronous task context. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// true if the client should make an SSL-wrapped connection to the server; otherwise, false. - /// The cancellation token. - /// - /// The is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - Task ConnectAsync (string host, int port, bool useSsl, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Establish a connection to the specified mail server. - /// - /// - /// Establish a connection to the specified mail server. - /// If a successful connection is made, the - /// property will be populated. - /// - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously establish a connection to the specified mail server. - /// - /// - /// Asynchronously establishes a connection to the specified mail server. - /// If a successful connection is made, the - /// property will be populated. - /// - /// An asynchronous task context. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// The is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - Task ConnectAsync (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Establish a connection to the specified mail server using the provided socket. - /// - /// - /// Establish a connection to the specified mail server using the provided socket. - /// If a successful connection is made, the - /// property will be populated. - /// - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - void Connect (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously establish a connection to the specified mail server using the provided socket. - /// - /// - /// Asynchronously establishes a connection to the specified mail server using the provided socket. - /// If a successful connection is made, the - /// property will be populated. - /// - /// An asynchronous task context. - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - Task ConnectAsync (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Establish a connection to the specified mail server using the provided stream. - /// - /// - /// Establish a connection to the specified mail server using the provided stream. - /// If a successful connection is made, the - /// property will be populated. - /// - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - void Connect (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously establish a connection to the specified mail server using the provided stream. - /// - /// - /// Asynchronously establishes a connection to the specified mail server using the provided stream. - /// If a successful connection is made, the - /// property will be populated. - /// - /// An asynchronous task context. - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - Task ConnectAsync (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Authenticate using the supplied credentials. - /// - /// - /// Authenticates using the supplied credentials. - /// If the server supports one or more SASL authentication mechanisms, then - /// the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - void Authenticate (ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously authenticate using the supplied credentials. - /// - /// - /// Asynchronously authenticates using the supplied credentials. - /// If the server supports one or more SASL authentication mechanisms, then - /// the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// - /// An asynchronous task context. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - Task AuthenticateAsync (ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Authenticate using the supplied credentials. - /// - /// - /// Authenticates using the supplied credentials. - /// If the server supports one or more SASL authentication mechanisms, then - /// the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// - /// The encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - void Authenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously authenticate using the supplied credentials. - /// - /// - /// Asynchronously authenticates using the supplied credentials. - /// If the server supports one or more SASL authentication mechanisms, then - /// the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// - /// An asynchronous task context. - /// The encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - Task AuthenticateAsync (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Authenticate using the specified user name and password. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// The encoding to use for the user's credentials. - /// The user name. - /// The password. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - void Authenticate (Encoding encoding, string userName, string password, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously authenticate using the specified user name and password. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// An asynchronous task context. - /// The encoding to use for the user's credentials. - /// The user name. - /// The password. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - Task AuthenticateAsync (Encoding encoding, string userName, string password, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Authenticate using the specified user name and password. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// - /// - /// - /// The user name. - /// The password. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - void Authenticate (string userName, string password, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously authenticate using the specified user name and password. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// An asynchronous task context. - /// The user name. - /// The password. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - Task AuthenticateAsync (string userName, string password, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - void Authenticate (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// An asynchronous task context. - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - Task AuthenticateAsync (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Disconnect the service. - /// - /// - /// Disconnects from the service. - /// If is true, a "QUIT" command will be issued in order to disconnect cleanly. - /// - /// If set to true, a "QUIT" command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - void Disconnect (bool quit, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously disconnect the service. - /// - /// - /// Asynchronously disconnects from the service. - /// If is true, a "QUIT" command will be issued in order to disconnect cleanly. - /// - /// An asynchronous task context. - /// If set to true, a logout/quit command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - Task DisconnectAsync (bool quit, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Ping the message service to keep the connection alive. - /// - /// - /// Mail servers, if left idle for too long, will automatically drop the connection. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - void NoOp (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously ping the mail server to keep the connection alive. - /// - /// - /// Mail servers, if left idle for too long, will automatically drop the connection. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - Task NoOpAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Occurs when the client has been successfully connected. - /// - /// - /// The event is raised when the client - /// successfully connects to the mail server. - /// - event EventHandler Connected; - - /// - /// Occurs when the client has been disconnected. - /// - /// - /// The event is raised whenever the client - /// has been disconnected. - /// - event EventHandler Disconnected; - - /// - /// Occurs when the client has been successfully authenticated. - /// - /// - /// The event is raised whenever the client - /// has been authenticated. - /// - event EventHandler Authenticated; - } -} diff --git a/src/MailKit/IMailSpool.cs b/src/MailKit/IMailSpool.cs deleted file mode 100644 index bc78eda..0000000 --- a/src/MailKit/IMailSpool.cs +++ /dev/null @@ -1,511 +0,0 @@ -// -// IMailSpool.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -namespace MailKit { - /// - /// An interface for retreiving messages from a spool. - /// - /// - /// An interface for retreiving messages from a spool. - /// - public interface IMailSpool : IMailService, IEnumerable - { - /// - /// Get the number of messages available in the message spool. - /// - /// - /// Gets the number of messages available in the message spool. - /// Once authenticated, the property will be set - /// to the number of available messages in the spool. - /// - /// The message count. - int Count { get; } - - /// - /// Get whether or not the service supports referencing messages by UIDs. - /// - /// - /// Not all servers support referencing messages by UID, so this property should - /// be checked before using - /// and . - /// If the server does not support UIDs, then all methods that take UID arguments - /// along with and - /// will fail. - /// - /// true if supports uids; otherwise, false. - bool SupportsUids { get; } - - /// - /// Get the message count. - /// - /// - /// Gets the message count. - /// - /// The message count. - /// The cancellation token. - int GetMessageCount (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the UID of the message at the specified index. - /// - /// - /// Not all servers support UIDs, so you should first check - /// the property. - /// - /// The message UID. - /// The message index. - /// The cancellation token. - string GetMessageUid (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the UID of the message at the specified index. - /// - /// - /// Not all servers support UIDs, so you should first check - /// the property. - /// - /// The message UID. - /// The message index. - /// The cancellation token. - Task GetMessageUidAsync (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the full list of available message UIDs. - /// - /// - /// Not all servers support UIDs, so you should first check - /// the property. - /// - /// The message UIDs. - /// The cancellation token. - IList GetMessageUids (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the full list of available message UIDs. - /// - /// - /// Not all servers support UIDs, so you should first check - /// the property. - /// - /// The message UIDs. - /// The cancellation token. - Task> GetMessageUidsAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the size of the specified message, in bytes. - /// - /// - /// Gets the size of the specified message, in bytes. - /// - /// The message size, in bytes. - /// The index of the message. - /// The cancellation token. - int GetMessageSize (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the size of the specified message, in bytes. - /// - /// - /// Asynchronously gets the size of the specified message, in bytes. - /// - /// The message size, in bytes. - /// The index of the message. - /// The cancellation token. - Task GetMessageSizeAsync (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the sizes for all available messages, in bytes. - /// - /// - /// Gets the sizes for all available messages, in bytes. - /// - /// The message sizes, in bytes. - /// The cancellation token. - IList GetMessageSizes (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the sizes for all available messages, in bytes. - /// - /// - /// Asynchronously gets the sizes for all available messages, in bytes. - /// - /// The message sizes, in bytes. - /// The cancellation token. - Task> GetMessageSizesAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the headers for the specified message. - /// - /// - /// Gets the headers for the specified message. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - HeaderList GetMessageHeaders (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the headers for the specified message. - /// - /// - /// Asynchronously gets the headers for the specified message. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - Task GetMessageHeadersAsync (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the headers for the specified messages. - /// - /// - /// Gets the headers for the specified messages. - /// - /// The headers for the specified messages. - /// The indexes of the messages. - /// The cancellation token. - IList GetMessageHeaders (IList indexes, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the headers for the specified messages. - /// - /// - /// Asynchronously gets the headers for the specified messages. - /// - /// The headers for the specified messages. - /// The indexes of the messages. - /// The cancellation token. - Task> GetMessageHeadersAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the headers of the messages within the specified range. - /// - /// - /// Gets the headers of the messages within the specified range. - /// - /// The headers of the messages within the specified range. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - IList GetMessageHeaders (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the headers of the messages within the specified range. - /// - /// - /// Gets the headers of the messages within the specified range. - /// - /// The headers of the messages within the specified range. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - Task> GetMessageHeadersAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the message at the specified index. - /// - /// - /// Gets the message at the specified index. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - MimeMessage GetMessage (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the message at the specified index. - /// - /// - /// Asynchronously gets the message at the specified index. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the messages at the specified indexes. - /// - /// - /// Gets the messages at the specified indexes. - /// - /// The messages. - /// The indexes of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - IList GetMessages (IList indexes, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the messages at the specified indexes. - /// - /// - /// Asynchronously gets the messages at the specified indexes. - /// - /// The messages. - /// The indexes of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the messages within the specified range. - /// - /// - /// Gets the messages within the specified range. - /// - /// The messages. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// The progress reporting mechanism. - IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the messages within the specified range. - /// - /// - /// Asynchronously gets the messages within the specified range. - /// - /// The messages. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// The progress reporting mechanism. - Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the message or header stream at the specified index. - /// - /// - /// Gets the message or header stream at the specified index. - /// - /// The message or header stream. - /// The index of the message. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the message or header stream at the specified index. - /// - /// - /// Asynchronously gets the message or header stream at the specified index. - /// - /// The message or header stream. - /// The index of the message. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the message or header streams at the specified index. - /// - /// - /// Gets the message or header streams at the specified index. - /// - /// The message or header streams. - /// The indexes of the messages. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the message or header streams at the specified indexes. - /// - /// - /// Asynchronously gets the message or header streams at the specified indexes. - /// - /// The message or header streams. - /// The indexes of the messages. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the message or header streams within the specified range. - /// - /// - /// Gets the message or header streams within the specified range. - /// - /// The message or header streams. - /// The index of the first stream to get. - /// The number of streams to get. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the message or header streams within the specified range. - /// - /// - /// Asynchronously gets the message or header streams within the specified range. - /// - /// The messages. - /// The index of the first stream to get. - /// The number of streams to get. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Mark the specified message for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The index of the message. - /// The cancellation token. - void DeleteMessage (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously mark the specified message for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The index of the message. - /// The cancellation token. - Task DeleteMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Mark the specified messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The indexes of the messages. - /// The cancellation token. - void DeleteMessages (IList indexes, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously mark the specified messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The cancellation token. - Task DeleteMessagesAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Mark the specified range of messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The index of the first message to mark for deletion. - /// The number of messages to mark for deletion. - /// The cancellation token. - void DeleteMessages (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously mark the specified range of messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The index of the first message to mark for deletion. - /// The number of messages to mark for deletion. - /// The cancellation token. - Task DeleteMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Mark all messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The cancellation token. - void DeleteAllMessages (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously mark all messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The cancellation token. - Task DeleteAllMessagesAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Reset the state of all messages marked for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The cancellation token. - void Reset (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously reset the state of all messages marked for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The cancellation token. - Task ResetAsync (CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MailKit/IMailStore.cs b/src/MailKit/IMailStore.cs deleted file mode 100644 index dca90aa..0000000 --- a/src/MailKit/IMailStore.cs +++ /dev/null @@ -1,549 +0,0 @@ -// -// IMailStore.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MailKit { - /// - /// An interface for retreiving messages from a message store such as IMAP. - /// - /// - /// Implemented by . - /// - public interface IMailStore : IMailService - { - /// - /// Get the personal namespaces. - /// - /// - /// The personal folder namespaces contain a user's personal mailbox folders. - /// - /// The personal namespaces. - FolderNamespaceCollection PersonalNamespaces { get; } - - /// - /// Get the shared namespaces. - /// - /// - /// The shared folder namespaces contain mailbox folders that are shared with the user. - /// - /// The shared namespaces. - FolderNamespaceCollection SharedNamespaces { get; } - - /// - /// Get the other namespaces. - /// - /// - /// The other folder namespaces contain other mailbox folders. - /// - /// The other namespaces. - FolderNamespaceCollection OtherNamespaces { get; } - - /// - /// Get whether or not the mail store supports quotas. - /// - /// - /// Gets whether or not the mail store supports quotas. - /// - /// true if the mail store supports quotas; otherwise, false. - bool SupportsQuotas { get; } - - /// - /// Get the threading algorithms supported by the mail store. - /// - /// - /// The threading algorithms are queried as part of the - /// Connect - /// and Authenticate methods. - /// - /// - /// - /// - /// The threading algorithms. - HashSet ThreadingAlgorithms { get; } - - /// - /// Get the Inbox folder. - /// - /// - /// The Inbox folder is the default folder and is typically the folder - /// where all new messages are delivered. - /// - /// The Inbox folder. - IMailFolder Inbox { get; } - - /// - /// Enable the quick resynchronization feature. - /// - /// - /// Enables quick resynchronization when a folder is opened using the - /// - /// method. - /// If this feature is enabled, the event - /// is replaced with the event. - /// This method needs to be called immediately after - /// , - /// before the opening of any folders. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Quick resynchronization needs to be enabled before selecting a folder. - /// - /// - /// The mail store does not support quick resynchronization. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - void EnableQuickResync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously enable the quick resynchronization feature. - /// - /// - /// Enables quick resynchronization when a folder is opened using the - /// - /// method. - /// If this feature is enabled, the event - /// is replaced with the event. - /// This method needs to be called immediately after - /// , - /// before the opening of any folders. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Quick resynchronization needs to be enabled before selecting a folder. - /// - /// - /// The mail store does not support quick resynchronization. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - Task EnableQuickResyncAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the specified special folder. - /// - /// - /// Not all message stores support the concept of special folders, - /// so this method may return null. - /// - /// The folder if available; otherwise null. - /// The type of special folder. - /// - /// is out of range. - /// - IMailFolder GetFolder (SpecialFolder folder); - - /// - /// Get the folder for the specified namespace. - /// - /// - /// The main reason to get the toplevel folder in a namespace is - /// to list its child folders. - /// - /// The folder. - /// The namespace. - /// - /// is null. - /// - /// - /// The folder could not be found. - /// - IMailFolder GetFolder (FolderNamespace @namespace); - - /// - /// Get all of the folders within the specified namespace. - /// - /// - /// Gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The namespace folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - IList GetFolders (FolderNamespace @namespace, bool subscribedOnly, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get all of the folders within the specified namespace. - /// - /// - /// Asynchronously gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The namespace folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - Task> GetFoldersAsync (FolderNamespace @namespace, bool subscribedOnly, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get all of the folders within the specified namespace. - /// - /// - /// Gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The namespace folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - IList GetFolders (FolderNamespace @namespace, StatusItems items = StatusItems.None, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get all of the folders within the specified namespace. - /// - /// - /// Asynchronously gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The namespace folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - Task> GetFoldersAsync (FolderNamespace @namespace, StatusItems items = StatusItems.None, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the folder for the specified path. - /// - /// - /// Gets the folder for the specified path. - /// - /// The folder. - /// The folder path. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - IMailFolder GetFolder (string path, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the folder for the specified path. - /// - /// - /// Asynchronously gets the folder for the specified path. - /// - /// The folder. - /// The folder path. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - Task GetFolderAsync (string path, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata tags. - /// The cancellation token. - MetadataCollection GetMetadata (IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata. - /// The metadata tags. - /// The cancellation token. - Task GetMetadataAsync (IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - MetadataCollection GetMetadata (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - Task GetMetadataAsync (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sets the specified metadata. - /// - /// - /// Sets the specified metadata. - /// - /// The metadata. - /// The cancellation token. - void SetMetadata (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sets the specified metadata. - /// - /// - /// Asynchronously sets the specified metadata. - /// - /// An asynchronous task context. - /// The metadata. - /// The cancellation token. - Task SetMetadataAsync (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Occurs when a remote message store receives an alert message from the server. - /// - /// - /// Some implementations, such as , - /// will emit Alert events when they receive alert messages from the server. - /// - event EventHandler Alert; - - /// - /// Occurs when a folder is created. - /// - /// - /// The event is emitted when a new folder is created. - /// - event EventHandler FolderCreated; - - /// - /// Occurs when metadata changes. - /// - /// - /// The event is emitted when metadata changes. - /// - event EventHandler MetadataChanged; - } -} diff --git a/src/MailKit/IMailTransport.cs b/src/MailKit/IMailTransport.cs deleted file mode 100644 index d51a88a..0000000 --- a/src/MailKit/IMailTransport.cs +++ /dev/null @@ -1,179 +0,0 @@ -// -// IMailTransport.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -namespace MailKit { - /// - /// An interface for sending messages. - /// - /// - /// An interface for sending messages. - /// - public interface IMailTransport : IMailService - { - /// - /// Send the specified message. - /// - /// - /// Sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - void Send (MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously send the specified message. - /// - /// - /// Asynchronously sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// An asynchronous task context. - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - Task SendAsync (MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Send the specified message using the supplied sender and recipients. - /// - /// - /// Sends the specified message using the supplied sender and recipients. - /// - /// The message. - /// The mailbox address to use for sending the message. - /// The mailbox addresses that should receive the message. - /// The cancellation token. - /// The progress reporting mechanism. - void Send (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously send the specified message using the supplied sender and recipients. - /// - /// - /// Asynchronously sends the specified message using the supplied sender and recipients. - /// - /// An asynchronous task context. - /// The message. - /// The mailbox address to use for sending the message. - /// 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 (CancellationToken), ITransferProgress progress = null); - - /// - /// Send the specified message. - /// - /// - /// Sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// The formatting options. - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - void Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously send the specified message. - /// - /// - /// Asynchronously sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// An asynchronous task context. - /// The formatting options. - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Send the specified message using the supplied sender and recipients. - /// - /// - /// Sends the specified message using the supplied sender and recipients. - /// - /// The formatting options. - /// The message. - /// The mailbox address to use for sending the message. - /// The mailbox addresses that should receive the message. - /// The cancellation token. - /// The progress reporting mechanism. - void Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously send the specified message using the supplied sender and recipients. - /// - /// - /// Asynchronously sends the specified message using the supplied sender and recipients. - /// - /// An asynchronous task context. - /// The formatting options. - /// The message. - /// The mailbox address to use for sending the message. - /// 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 (CancellationToken), ITransferProgress progress = null); - - /// - /// Occurs when a message is successfully sent via the transport. - /// - /// - /// The event will be emitted each time a message is successfully sent. - /// - event EventHandler MessageSent; - } -} diff --git a/src/MailKit/IMessageSummary.cs b/src/MailKit/IMessageSummary.cs deleted file mode 100644 index 6a90e93..0000000 --- a/src/MailKit/IMessageSummary.cs +++ /dev/null @@ -1,460 +0,0 @@ -// -// IMessageSummary.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -using MimeKit; - -namespace MailKit { - /// - /// A summary of a message. - /// - /// - /// The Fetch and - /// FetchAsync methods - /// return lists of items. - /// The properties of the that will be available - /// depend on the passed to the aformentioned method. - /// - public interface IMessageSummary - { - /// - /// Get the folder that the message belongs to. - /// - /// - /// Gets the folder that the message belongs to, if available. - /// - /// The folder. - IMailFolder Folder { - get; - } - - /// - /// Get a bitmask of fields that have been populated. - /// - /// - /// Gets a bitmask of fields that have been populated. - /// - /// The fields that have been populated. - MessageSummaryItems Fields { get; } - - /// - /// Gets the body structure of the message, if available. - /// - /// - /// The body will be one of , - /// , , - /// or . - /// This property will only be set if either the - /// flag or the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The body structure of the message. - BodyPart Body { get; } - - /// - /// Gets the text body part of the message if it exists. - /// - /// - /// Gets the text/plain body part of the message. - /// This property will only be usable if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// - /// - /// - /// The text body if it exists; otherwise, null. - BodyPartText TextBody { get; } - - /// - /// Gets the html body part of the message if it exists. - /// - /// - /// Gets the text/html body part of the message. - /// This property will only be usable if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The html body if it exists; otherwise, null. - BodyPartText HtmlBody { get; } - - /// - /// Gets the body parts of the message. - /// - /// - /// Traverses over the , enumerating all of the - /// objects. - /// This property will only be usable if either the - /// flag or the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The body parts. - IEnumerable BodyParts { get; } - - /// - /// Gets the attachments. - /// - /// - /// Traverses over the , enumerating all of the - /// objects that have a Content-Disposition - /// header set to "attachment". - /// This property will only be usable if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// - /// - /// - /// The attachments. - IEnumerable Attachments { get; } - - /// - /// Gets the preview text of the message. - /// - /// - /// The preview text is a short snippet of the beginning of the message - /// text, typically shown in a mail client's message list to provide the user - /// with a sense of what the message is about. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The preview text. - string PreviewText { get; } - - /// - /// Gets the envelope of the message, if available. - /// - /// - /// The envelope of a message contains information such as the - /// date the message was sent, the subject of the message, - /// the sender of the message, who the message was sent to, - /// which message(s) the message may be in reply to, - /// and the message id. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The envelope of the message. - Envelope Envelope { get; } - - /// - /// Gets the normalized subject. - /// - /// - /// A normalized Subject header value where prefixes such as "Re:", "Re[#]:" and "FWD:" have been pruned. - /// This property is typically used for threading messages by subject. - /// - /// The normalized subject. - string NormalizedSubject { get; } - - /// - /// Gets the Date header value. - /// - /// - /// Gets the Date header value. If the Date header is not present, the arrival date is used. - /// If neither are known, is returned. - /// - /// The date. - DateTimeOffset Date { get; } - - /// - /// Gets whether or not the message is a reply. - /// - /// - /// This value should be based on whether the message subject contained any "Re:", "Re[#]:" or "FWD:" prefixes. - /// - /// true if the message is a reply; otherwise, false. - bool IsReply { get; } - - /// - /// Gets the message flags, if available. - /// - /// - /// Gets the message flags, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The message flags. - MessageFlags? Flags { get; } - - /// - /// Gets the user-defined message flags, if available. - /// - /// - /// Gets the user-defined message flags, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The user-defined message flags. - HashSet Keywords { get; } - - /// - /// Gets the user-defined message flags, if available. - /// - /// - /// Gets the user-defined message flags, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The user-defined message flags. - [Obsolete ("Use Keywords instead.")] - HashSet UserFlags { get; } - - /// - /// Gets the message annotations, if available. - /// - /// - /// Gets the message annotations, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The message annotations. - IList Annotations { get; } - - /// - /// Gets the list of headers, if available. - /// - /// - /// Gets the list of headers, if available. - /// This property will only be set if - /// is specified in a call to one of the - /// Fetch - /// or FetchAsync - /// methods or specific headers are requested via a one of the Fetch or FetchAsync methods - /// that accept list of specific headers to request for each message such as - /// . - /// - /// - /// The list of headers. - HeaderList Headers { get; } - - /// - /// Gets the internal date of the message, if available. - /// - /// - /// Gets the internal date of the message (often the same date as found in the Received header), if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The internal date of the message. - DateTimeOffset? InternalDate { get; } - - /// - /// Gets the size of the message, in bytes, if available. - /// - /// - /// Gets the size of the message, in bytes, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The size of the message. - uint? Size { get; } - - /// - /// Gets the mod-sequence value for the message, if available. - /// - /// - /// Gets the mod-sequence value for the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The mod-sequence value. - ulong? ModSeq { get; } - - /// - /// Gets the message-ids that the message references, if available. - /// - /// - /// Gets the message-ids that the message references, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The references. - MessageIdList References { get; } - - /// - /// Get the globally unique identifier for the message, if available. - /// - /// - /// Gets the globally unique identifier of the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// This property maps to the EMAILID value defined in the - /// OBJECTID extension. - /// - /// The globally unique message identifier. - string EmailId { get; } - - /// - /// Get the globally unique identifier for the message, if available. - /// - /// - /// Gets the globally unique identifier of the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// This property maps to the EMAILID value defined in the - /// OBJECTID extension. - /// - /// The globally unique message identifier. - [Obsolete ("Use EmailId instead.")] - string Id { get; } - - /// - /// Get the globally unique thread identifier for the message, if available. - /// - /// - /// Gets the globally unique thread identifier for the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// This property maps to the THREADID value defined in the - /// OBJECTID extension. - /// - /// The globally unique thread identifier. - string ThreadId { get; } - - /// - /// Gets the unique identifier of the message, if available. - /// - /// - /// Gets the unique identifier of the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The uid of the message. - UniqueId UniqueId { get; } - - /// - /// Gets the index of the message. - /// - /// - /// Gets the index of the message. - /// This property is always set. - /// - /// The index of the message. - int Index { get; } - - #region GMail extension properties - - /// - /// Gets the GMail message identifier, if available. - /// - /// - /// Gets the GMail message identifier, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The GMail message identifier. - ulong? GMailMessageId { get; } - - /// - /// Gets the GMail thread identifier, if available. - /// - /// - /// Gets the GMail thread identifier, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The GMail thread identifier. - ulong? GMailThreadId { get; } - - /// - /// Gets the list of GMail labels, if available. - /// - /// - /// Gets the list of GMail labels, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The GMail labels. - IList GMailLabels { get; } - - #endregion - } -} diff --git a/src/MailKit/IProtocolLogger.cs b/src/MailKit/IProtocolLogger.cs deleted file mode 100644 index 03c07d7..0000000 --- a/src/MailKit/IProtocolLogger.cs +++ /dev/null @@ -1,116 +0,0 @@ -// -// IProtocolLogger.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// An interface for logging the communication between a client and server. - /// - /// - /// An interface for logging the communication between a client and server. - /// - /// - /// - /// - public interface IProtocolLogger : IDisposable - { - /// - /// Logs a connection to the specified URI. - /// - /// - /// Logs a connection to the specified URI. - /// - /// The URI. - /// - /// is null. - /// - /// - /// The logger has been disposed. - /// - /// - /// An I/O error occurred. - /// - void LogConnect (Uri uri); - - /// - /// Logs a sequence of bytes sent by the client. - /// - /// - /// Logs a sequence of bytes sent by the client. - /// is called by the upon every successful - /// write operation to its underlying network stream, passing the exact same , - /// , and arguments to the logging function. - /// - /// The buffer to log. - /// The offset of the first byte to log. - /// The number of bytes to log. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The logger has been disposed. - /// - /// - /// An I/O error occurred. - /// - void LogClient (byte[] buffer, int offset, int count); - - /// - /// Logs a sequence of bytes sent by the server. - /// - /// - /// Logs a sequence of bytes sent by the server. - /// is called by the upon every successful - /// read of its underlying network stream with the exact buffer that was read. - /// - /// The buffer to log. - /// The offset of the first byte to log. - /// The number of bytes to log. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The logger has been disposed. - /// - /// - /// An I/O error occurred. - /// - void LogServer (byte[] buffer, int offset, int count); - } -} diff --git a/src/MailKit/ITransferProgress.cs b/src/MailKit/ITransferProgress.cs deleted file mode 100644 index 4a6ecb9..0000000 --- a/src/MailKit/ITransferProgress.cs +++ /dev/null @@ -1,58 +0,0 @@ -// -// ITransferProgress.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit { - /// - /// An interface for reporting progress of uploading or downloading messages. - /// - /// - /// An interface for reporting progress of uploading or downloading messages. - /// - public interface ITransferProgress - { - /// - /// Report the progress of the transfer operation. - /// - /// - /// Reports the progress of the transfer operation. - /// This method is only used if the operation knows the size - /// of the message, part, or stream being transferred without doing - /// extra work to calculate it. - /// - /// The number of bytes transferred. - /// The total size, in bytes, of the message, part, or stream being transferred. - void Report (long bytesTransferred, long totalSize); - - /// - /// Report the progress of the transfer operation. - /// - /// - /// Reports the progress of the transfer operation. - /// - /// The number of bytes transferred. - void Report (long bytesTransferred); - } -} diff --git a/src/MailKit/MailFolder.cs b/src/MailKit/MailFolder.cs deleted file mode 100644 index 9cd9ef8..0000000 --- a/src/MailKit/MailFolder.cs +++ /dev/null @@ -1,16501 +0,0 @@ -// -// MailFolder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Collections; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -using MailKit.Search; - -namespace MailKit { - /// - /// An abstract mail folder implementation. - /// - /// - /// An abstract mail folder implementation. - /// - public abstract class MailFolder : IMailFolder - { - /// - /// The bit mask of settable flags. - /// - /// - /// Only flags in the list of settable flags may be set on a message by the client. - /// - protected static readonly MessageFlags SettableFlags = MessageFlags.Answered | MessageFlags.Deleted | - MessageFlags.Draft | MessageFlags.Flagged | MessageFlags.Seen; - - IMailFolder parent; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - protected MailFolder () - { - } - - /// - /// Get an object that can be used to synchronize access to the folder. - /// - /// - /// Gets an object that can be used to synchronize access to the folder. - /// - /// The sync root. - public abstract object SyncRoot { - get; - } - - /// - /// Get the parent folder. - /// - /// - /// Root-level folders do not have a parent folder. - /// - /// The parent folder. - public IMailFolder ParentFolder { - get { return parent; } - internal protected set { - if (value == parent) - return; - - if (parent != null) - parent.Renamed -= OnParentFolderRenamed; - - parent = value; - - if (parent != null) - parent.Renamed += OnParentFolderRenamed; - } - } - - /// - /// Get the folder attributes. - /// - /// - /// Gets the folder attributes. - /// - /// The folder attributes. - public FolderAttributes Attributes { - get; internal protected set; - } - - /// - /// Get the annotation access level. - /// - /// - /// If annotations are supported, this property can be used to determine whether or not - /// the supports reading and writing annotations. - /// - public AnnotationAccess AnnotationAccess { - get; internal protected set; - } - - /// - /// Get the supported annotation scopes. - /// - /// - /// If annotations are supported, this property can be used to determine which - /// annotation scopes are supported by the . - /// - public AnnotationScope AnnotationScopes { - get; internal protected set; - } - - /// - /// Get the maximum size of annotation values supported by the folder. - /// - /// - /// If annotations are supported, this property can be used to determine the - /// maximum size of annotation values supported by the . - /// - public uint MaxAnnotationSize { - get; internal protected set; - } - - /// - /// Get the permanent flags. - /// - /// - /// The permanent flags are the message flags that will persist between sessions. - /// If the flag is set, then the folder allows - /// storing of user-defined (custom) message flags. - /// - /// The permanent flags. - public MessageFlags PermanentFlags { - get; protected set; - } - - /// - /// Get the accepted flags. - /// - /// - /// The accepted flags are the message flags that will be accepted and persist - /// for the current session. For the set of flags that will persist between - /// sessions, see the property. - /// - /// The accepted flags. - public MessageFlags AcceptedFlags { - get; protected set; - } - - /// - /// Get the directory separator. - /// - /// - /// Gets the directory separator. - /// - /// The directory separator. - public char DirectorySeparator { - get; protected set; - } - - /// - /// Get the read/write access of the folder. - /// - /// - /// Gets the read/write access of the folder. - /// - /// The read/write access. - public FolderAccess Access { - get; internal protected set; - } - - /// - /// Get whether or not the folder is a namespace folder. - /// - /// - /// Gets whether or not the folder is a namespace folder. - /// - /// true if the folder is a namespace folder; otherwise, false. - public bool IsNamespace { - get; protected set; - } - - /// - /// Get the full name of the folder. - /// - /// - /// This is the equivalent of the full path of a file on a file system. - /// - /// The full name of the folder. - public string FullName { - get; protected set; - } - - /// - /// Get the name of the folder. - /// - /// - /// This is the equivalent of the file name of a file on the file system. - /// - /// The name of the folder. - public string Name { - get; protected set; - } - - /// - /// Get the unique identifier for the folder, if available. - /// - /// - /// Gets a unique identifier for the folder, if available. This is useful for clients - /// implementing a message cache that want to track the folder after it is renamed by another - /// client. - /// This property will only be available if the server supports the - /// OBJECTID extension. - /// - /// The unique folder identifier. - public string Id { - get; protected set; - } - - /// - /// Get a value indicating whether the folder is subscribed. - /// - /// - /// Gets a value indicating whether the folder is subscribed. - /// - /// true if the folder is subscribed; otherwise, false. - public bool IsSubscribed { - get { return (Attributes & FolderAttributes.Subscribed) != 0; } - } - - /// - /// Get a value indicating whether the folder is currently open. - /// - /// - /// Gets a value indicating whether the folder is currently open. - /// - /// true if the folder is currently open; otherwise, false. - public abstract bool IsOpen { - get; - } - - /// - /// Get a value indicating whether the folder exists. - /// - /// - /// Gets a value indicating whether the folder exists. - /// - /// true if the folder exists; otherwise, false. - public bool Exists { - get { return (Attributes & FolderAttributes.NonExistent) == 0; } - } - - /// - /// Get whether or not the folder supports mod-sequences. - /// - /// - /// Gets whether or not the folder supports mod-sequences. - /// If mod-sequences are not supported by the folder, then all of the APIs that take a modseq - /// argument will throw and should not be used. - /// - /// true if supports mod-sequences; otherwise, false. - [Obsolete ("Use Supports (FolderFeature.ModSequences) instead.")] - public bool SupportsModSeq { - get { return Supports (FolderFeature.ModSequences); } - } - - /// - /// Get the highest mod-sequence value of all messages in the mailbox. - /// - /// - /// Gets the highest mod-sequence value of all messages in the mailbox. - /// - /// The highest mod-sequence value. - public ulong HighestModSeq { - get; protected set; - } - - /// - /// Get the UID validity. - /// - /// - /// UIDs are only valid so long as the UID validity value remains unchanged. If and when - /// the folder's is changed, a client MUST discard its cache of UIDs - /// along with any summary information that it may have and re-query the folder. - /// This value will only be set after the folder has been opened. - /// - /// The UID validity. - public uint UidValidity { - get; protected set; - } - - /// - /// Get the UID that the folder will assign to the next message that is added. - /// - /// - /// This value will only be set after the folder has been opened. - /// - /// The next UID. - public UniqueId? UidNext { - get; protected set; - } - - /// - /// Get the maximum size of a message that can be appended to the folder. - /// - /// - /// Gets the maximum size of a message that can be appended to the folder. - /// If the value is not set, then the limit is unspecified. - /// - /// The append limit. - public uint? AppendLimit { - get; protected set; - } - - /// - /// Get the size of the folder. - /// - /// - /// Gets the size of the folder in bytes. - /// If the value is not set, then the size is unspecified. - /// - /// The size. - public ulong? Size { - get; protected set; - } - - /// - /// Get the index of the first unread message in the folder. - /// - /// - /// This value will only be set after the folder has been opened. - /// - /// The index of the first unread message. - public int FirstUnread { - get; protected set; - } - - /// - /// Get the number of unread messages in the folder. - /// - /// - /// Gets the number of unread messages in the folder. - /// This value will only be set after calling - /// - /// with . - /// - /// The number of unread messages. - public int Unread { - get; protected set; - } - - /// - /// Get the number of recently added messages in the folder. - /// - /// - /// Gets the number of recently delivered messages in the folder. - /// This value will only be set after calling - /// - /// with or by opening the folder. - /// - /// The number of recently added messages. - public int Recent { - get; protected set; - } - - /// - /// Get the total number of messages in the folder. - /// - /// - /// Gets the total number of messages in the folder. - /// This value will only be set after calling - /// - /// with or by opening the folder. - /// - /// The total number of messages. - public int Count { - get; protected set; - } - - /// - /// Get the threading algorithms supported by the folder. - /// - /// - /// Get the threading algorithms supported by the folder. - /// - /// The supported threading algorithms. - public abstract HashSet ThreadingAlgorithms { get; } - - /// - /// Determine whether or not a supports a feature. - /// - /// - /// Determines whether or not a supports a feature. - /// - /// The desired feature. - /// true if the feature is supported; otherwise, false. - public abstract bool Supports (FolderFeature feature); - - /// - /// Opens the folder using the requested folder access. - /// - /// - /// This variant of the - /// method is meant for quick resynchronization of the folder. Before calling this method, - /// the method MUST be called. - /// You should also make sure to add listeners to the and - /// events to get notifications of changes since - /// the last time the folder was opened. - /// - /// The state of the folder. - /// The requested folder access. - /// The last known value. - /// The last known value. - /// The last known list of unique message identifiers. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The quick resynchronization feature has not been enabled. - /// - /// - /// The mail store does not support the quick resynchronization feature. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract FolderAccess Open (FolderAccess access, uint uidValidity, ulong highestModSeq, IList uids, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously opens the folder using the requested folder access. - /// - /// - /// This variant of the - /// method is meant for quick resynchronization of the folder. Before calling this method, - /// the method MUST be called. - /// You should also make sure to add listeners to the and - /// events to get notifications of changes since - /// the last time the folder was opened. - /// - /// The state of the folder. - /// The requested folder access. - /// The last known value. - /// The last known value. - /// The last known list of unique message identifiers. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The quick resynchronization feature has not been enabled. - /// - /// - /// The mail store does not support the quick resynchronization feature. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task OpenAsync (FolderAccess access, uint uidValidity, ulong highestModSeq, IList uids, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Open the folder using the requested folder access. - /// - /// - /// Opens the folder using the requested folder access. - /// - /// The state of the folder. - /// The requested folder access. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract FolderAccess Open (FolderAccess access, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously open the folder using the requested folder access. - /// - /// - /// Asynchronously opens the folder using the requested folder access. - /// - /// The state of the folder. - /// The requested folder access. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task OpenAsync (FolderAccess access, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Close the folder, optionally expunging the messages marked for deletion. - /// - /// - /// Closes the folder, optionally expunging the messages marked for deletion. - /// - /// If set to true, expunge. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Close (bool expunge = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously close the folder, optionally expunging the messages marked for deletion. - /// - /// - /// Asynchronously closes the folder, optionally expunging the messages marked for deletion. - /// - /// An asynchronous task context. - /// If set to true, expunge. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task CloseAsync (bool expunge = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// true if the folder will be used to contain messages; otherwise false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IMailFolder Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously create a new subfolder with the given name. - /// - /// - /// Asynchronously creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// true if the folder will be used to contain messages; otherwise false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// A list of special uses for the folder being created. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The does not support the creation of special folders. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IMailFolder Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously create a new subfolder with the given name. - /// - /// - /// Asynchronously creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// A list of special uses for the folder being created. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The does not support the creation of special folders. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// The special use for the folder being created. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The does not support the creation of special folders. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IMailFolder Create (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default (CancellationToken)) - { - return Create (name, new [] { specialUse }, cancellationToken); - } - - /// - /// Asynchronously create a new subfolder with the given name. - /// - /// - /// Asynchronously creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// The special use for the folder being created. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The does not support the creation of special folders. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task CreateAsync (string name, SpecialFolder specialUse, CancellationToken cancellationToken = default (CancellationToken)) - { - return CreateAsync (name, new [] { specialUse }, cancellationToken); - } - - /// - /// Rename the folder. - /// - /// - /// Renames the folder. - /// - /// The new parent folder. - /// The new name of the folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not belong to the . - /// -or- - /// is not a legal folder name. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder cannot be renamed (it is either a namespace or the Inbox). - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Rename (IMailFolder parent, string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously rename the folder. - /// - /// - /// Asynchronously renames the folder. - /// - /// An asynchronous task context. - /// The new parent folder. - /// The new name of the folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not belong to the . - /// -or- - /// is not a legal folder name. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder cannot be renamed (it is either a namespace or the Inbox). - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task RenameAsync (IMailFolder parent, string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Delete the folder. - /// - /// - /// Deletes the folder. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder cannot be deleted (it is either a namespace or the Inbox). - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Delete (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously delete the folder. - /// - /// - /// Asynchronously deletes the folder. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder cannot be deleted (it is either a namespace or the Inbox). - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task DeleteAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Subscribe to the folder. - /// - /// - /// Subscribes to the folder. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Subscribe (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously subscribe to the folder. - /// - /// - /// Asynchronously subscribes to the folder. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SubscribeAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Unsubscribe from the folder. - /// - /// - /// Unsubscribes from the folder. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Unsubscribe (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously unsubscribe from the folder. - /// - /// - /// Asynchronously unsubscribes from the folder. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task UnsubscribeAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the subfolders. - /// - /// - /// Gets the subfolders as well as queries the server for the status of the requested items. - /// When the argument is non-empty, this has the equivalent functionality - /// of calling and then calling - /// on each of the returned folders. - /// - /// The subfolders. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList GetSubfolders (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the subfolders. - /// - /// - /// Asynchronously gets the subfolders as well as queries the server for the status of the requested items. - /// When the argument is non-empty, this has the equivalent functionality - /// of calling and then calling - /// on each of the returned folders. - /// - /// The subfolders. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> GetSubfoldersAsync (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the subfolders. - /// - /// - /// Gets the subfolders. - /// - /// The subfolders. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList GetSubfolders (bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetSubfolders (StatusItems.None, subscribedOnly, cancellationToken); - } - - /// - /// Asynchronously get the subfolders. - /// - /// - /// Asynchronously gets the subfolders. - /// - /// The subfolders. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> GetSubfoldersAsync (bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetSubfoldersAsync (StatusItems.None, subscribedOnly, cancellationToken); - } - - /// - /// Get the specified subfolder. - /// - /// - /// Gets the specified subfolder. - /// - /// The subfolder. - /// The name of the subfolder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is either an empty string or contains the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The requested folder could not be found. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IMailFolder GetSubfolder (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the specified subfolder. - /// - /// - /// Asynchronously gets the specified subfolder. - /// - /// The subfolder. - /// The name of the subfolder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is either an empty string or contains the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The requested folder could not be found. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetSubfolderAsync (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Force the server to flush its state for the folder. - /// - /// - /// Forces the server to flush its state for the folder. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Check (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously force the server to flush its state for the folder. - /// - /// - /// Asynchronously forces the server to flush its state for the folder. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task CheckAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Update the values of the specified items. - /// - /// - /// Updates the values of the specified items. - /// The method - /// MUST NOT be used on a folder that is already in the opened state. Instead, other ways - /// of getting the desired information should be used. - /// For example, a common use for the - /// method is to get the number of unread messages in the folder. When the folder is open, however, it is - /// possible to use the - /// method to query for the list of unread messages. - /// - /// The items to update. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The mail store does not support the STATUS command. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Status (StatusItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously update the values of the specified items. - /// - /// - /// Updates the values of the specified items. - /// The method - /// MUST NOT be used on a folder that is already in the opened state. Instead, other ways - /// of getting the desired information should be used. - /// For example, a common use for the - /// method is to get the number of unread messages in the folder. When the folder is open, however, it is - /// possible to use the - /// method to query for the list of unread messages. - /// - /// An asynchronous task context. - /// The items to update. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the STATUS command. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task StatusAsync (StatusItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the complete access control list for the folder. - /// - /// - /// Gets the complete access control list for the folder. - /// - /// The access control list. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract AccessControlList GetAccessControlList (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the complete access control list for the folder. - /// - /// - /// Asynchronously gets the complete access control list for the folder. - /// - /// The access control list. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetAccessControlListAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the access rights for a particular identifier. - /// - /// - /// Gets the access rights for a particular identifier. - /// - /// The access rights. - /// The identifier name. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract AccessRights GetAccessRights (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the access rights for a particular identifier. - /// - /// - /// Asynchronously gets the access rights for a particular identifier. - /// - /// The access rights. - /// The identifier name. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetAccessRightsAsync (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the access rights for the current authenticated user. - /// - /// - /// Gets the access rights for the current authenticated user. - /// - /// The access rights. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract AccessRights GetMyAccessRights (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the access rights for the current authenticated user. - /// - /// - /// Asynchronously gets the access rights for the current authenticated user. - /// - /// The access rights. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetMyAccessRightsAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add access rights for the specified identity. - /// - /// - /// Adds the given access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void AddAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add access rights for the specified identity. - /// - /// - /// Asynchronously adds the given access rights for the specified identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task AddAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove access rights for the specified identity. - /// - /// - /// Removes the given access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void RemoveAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove access rights for the specified identity. - /// - /// - /// Asynchronously removes the given access rights for the specified identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task RemoveAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the access rights for the specified identity. - /// - /// - /// Sets the access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void SetAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the access rights for the specified identity. - /// - /// - /// Asynchronously sets the access rights for the specified identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SetAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove all access rights for the given identity. - /// - /// - /// Removes all access rights for the given identity. - /// - /// The identity name. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void RemoveAccess (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove all access rights for the given identity. - /// - /// - /// Asynchronously removes all access rights for the given identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task RemoveAccessAsync (string name, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the quota information for the folder. - /// - /// - /// Gets the quota information for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The folder quota. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support quotas. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract FolderQuota GetQuota (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the quota information for the folder. - /// - /// - /// Asynchronously gets the quota information for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The folder quota. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support quotas. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetQuotaAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the quota limits for the folder. - /// - /// - /// Sets the quota limits for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The updated folder quota. - /// If not null, sets the maximum number of messages to allow. - /// If not null, sets the maximum storage size (in kilobytes). - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support quotas. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract FolderQuota SetQuota (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the quota limits for the folder. - /// - /// - /// Asynchronously sets the quota limits for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The updated folder quota. - /// If not null, sets the maximum number of messages to allow. - /// If not null, sets the maximum storage size (in kilobytes). - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail store does not support quotas. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SetQuotaAsync (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public MetadataCollection GetMetadata (IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadata (new MetadataOptions (), tags, cancellationToken); - } - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task GetMetadataAsync (IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (new MetadataOptions (), tags, cancellationToken); - } - - /// - /// Get the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract MetadataCollection GetMetadata (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetMetadataAsync (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sets the specified metadata. - /// - /// - /// Sets the specified metadata. - /// - /// The metadata. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void SetMetadata (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sets the specified metadata. - /// - /// - /// Asynchronously sets the specified metadata. - /// - /// An asynchronous task context. - /// The metadata. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SetMetadataAsync (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Expunge the folder, permanently removing all messages marked for deletion. - /// - /// - /// Expunges the folder, permanently removing all messages marked for deletion. - /// Normally, an event will be emitted for - /// each message that is expunged. However, if the mail store supports the quick - /// resynchronization feature and it has been enabled via the - /// method, then - /// the event will be emitted rather than the - /// event. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Expunge (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously expunge the folder, permanently removing all messages marked for deletion. - /// - /// - /// Asynchronously expunges the folder, permanently removing all messages marked for deletion. - /// Normally, an event will be emitted for - /// each message that is expunged. However, if the mail store supports the quick - /// resynchronization feature and it has been enabled via the - /// method, then - /// the event will be emitted rather than the - /// event. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task ExpungeAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Expunge the specified uids, permanently removing them from the folder. - /// - /// - /// Expunges the specified uids, permanently removing them from the folder. - /// Normally, an event will be emitted for - /// each message that is expunged. However, if the mail store supports the quick - /// resynchronization feature and it has been enabled via the - /// method, then - /// the event will be emitted rather than the - /// event. - /// - /// The message uids. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Expunge (IList uids, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously expunge the specified uids, permanently removing them from the folder. - /// - /// - /// Asynchronously expunges the specified uids, permanently removing them from the folder. - /// Normally, an event will be emitted for - /// each message that is expunged. However, if the mail store supports the quick - /// resynchronization feature and it has been enabled via the - /// method, then - /// the event will be emitted rather than the - /// event. - /// - /// An asynchronous task context. - /// The message uids. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task ExpungeAsync (IList uids, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual UniqueId? Append (MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Append (FormatOptions.Default, message, flags, cancellationToken, progress); - } - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AppendAsync (MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (FormatOptions.Default, message, flags, cancellationToken, progress); - } - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual UniqueId? Append (MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Append (FormatOptions.Default, message, flags, date, cancellationToken, progress); - } - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AppendAsync (MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (FormatOptions.Default, message, flags, date, cancellationToken, progress); - } - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// One or more does not define any properties. - /// " - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual UniqueId? Append (MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Append (FormatOptions.Default, message, flags, date, annotations, cancellationToken, progress); - } - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// One or more does not define any properties. - /// " - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AppendAsync (MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (FormatOptions.Default, message, flags, date, annotations, cancellationToken, progress); - } - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Asynchronously appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The array of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages does not match the number of flags. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList Append (IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Append (FormatOptions.Default, messages, flags, cancellationToken, progress); - } - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Asynchronously appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The array of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages does not match the number of flags. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> AppendAsync (IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (FormatOptions.Default, messages, flags, cancellationToken, progress); - } - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The array of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages, flags, and dates do not match. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList Append (IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Append (FormatOptions.Default, messages, flags, dates, cancellationToken, progress); - } - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Asynchronously appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The array of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages, flags, and dates do not match. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> AppendAsync (IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (FormatOptions.Default, messages, flags, dates, cancellationToken, progress); - } - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The array of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages does not match the number of flags. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Append (FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Asynchronously appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The array of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages does not match the number of flags. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> AppendAsync (FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The array of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages, flags, and dates do not match. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Append (FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Asynchronously appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The array of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages, flags, and dates do not match. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> AppendAsync (FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual UniqueId? Replace (UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Replace (FormatOptions.Default, uid, message, flags, cancellationToken, progress); - } - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task ReplaceAsync (UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (FormatOptions.Default, uid, message, flags, cancellationToken, progress); - } - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual UniqueId? Replace (UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Replace (FormatOptions.Default, uid, message, flags, date, cancellationToken, progress); - } - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task ReplaceAsync (UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (FormatOptions.Default, uid, message, flags, date, cancellationToken, progress); - } - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public abstract UniqueId? Replace (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public abstract Task ReplaceAsync (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public abstract UniqueId? Replace (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public abstract Task ReplaceAsync (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual UniqueId? Replace (int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Replace (FormatOptions.Default, index, message, flags, cancellationToken, progress); - } - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task ReplaceAsync (int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (FormatOptions.Default, index, message, flags, cancellationToken, progress); - } - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual UniqueId? Replace (int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return Replace (FormatOptions.Default, index, message, flags, date, cancellationToken, progress); - } - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task ReplaceAsync (int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (FormatOptions.Default, index, message, flags, date, cancellationToken, progress); - } - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public abstract UniqueId? Replace (FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public abstract Task ReplaceAsync (FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public abstract UniqueId? Replace (FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public abstract Task ReplaceAsync (FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Copy the specified message to the destination folder. - /// - /// - /// Copies the specified message to the destination folder. - /// - /// The UID of the message in the destination folder, if available; otherwise, null. - /// The UID of the message to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The mail store does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual UniqueId? CopyTo (UniqueId uid, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - var uids = CopyTo (new [] { uid }, destination, cancellationToken); - - if (uids != null && uids.Destination.Count > 0) - return uids.Destination[0]; - - return null; - } - - /// - /// Asynchronously copy the specified message to the destination folder. - /// - /// - /// Asynchronously copies the specified message to the destination folder. - /// - /// The UID of the message in the destination folder, if available; otherwise, null. - /// The UID of the message to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The mail store does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual async Task CopyToAsync (UniqueId uid, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - var uids = await CopyToAsync (new [] { uid }, destination, cancellationToken).ConfigureAwait (false); - - if (uids != null && uids.Destination.Count > 0) - return uids.Destination[0]; - - return null; - } - - /// - /// Copy the specified messages to the destination folder. - /// - /// - /// Copies the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The mail store does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract UniqueIdMap CopyTo (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously copy the specified messages to the destination folder. - /// - /// - /// Asynchronously copies the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The mail store does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task CopyToAsync (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Move the specified message to the destination folder. - /// - /// - /// Moves the specified message to the destination folder. - /// - /// The UID of the message in the destination folder, if available; otherwise, null. - /// The UID of the message to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The mail store does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual UniqueId? MoveTo (UniqueId uid, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - var uids = MoveTo (new [] { uid }, destination, cancellationToken); - - if (uids != null && uids.Destination.Count > 0) - return uids.Destination[0]; - - return null; - } - - /// - /// Asynchronously move the specified message to the destination folder. - /// - /// - /// Asynchronously moves the specified message to the destination folder. - /// - /// The UID of the message in the destination folder, if available; otherwise, null. - /// The UID of the message to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The mail store does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual async Task MoveToAsync (UniqueId uid, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - var uids = await MoveToAsync (new [] { uid }, destination, cancellationToken).ConfigureAwait (false); - - if (uids != null && uids.Destination.Count > 0) - return uids.Destination[0]; - - return null; - } - - /// - /// Move the specified messages to the destination folder. - /// - /// - /// Moves the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The mail store does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract UniqueIdMap MoveTo (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously move the specified messages to the destination folder. - /// - /// - /// Asynchronously moves the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The mail store does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task MoveToAsync (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Copy the specified message to the destination folder. - /// - /// - /// Copies the specified message to the destination folder. - /// - /// The index of the message to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// does not refer to a valid message index. - /// - /// - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void CopyTo (int index, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - CopyTo (new [] { index }, destination, cancellationToken); - } - - /// - /// Asynchronously copy the specified message to the destination folder. - /// - /// - /// Asynchronously copies the specified message to the destination folder. - /// - /// An asynchronous task context. - /// The indexes of the message to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// does not refer to a valid message index. - /// - /// - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task CopyToAsync (int index, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - return CopyToAsync (new [] { index }, destination, cancellationToken); - } - - /// - /// Copy the specified messages to the destination folder. - /// - /// - /// Copies the specified messages to the destination folder. - /// - /// The indexes of the messages to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void CopyTo (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously copy the specified messages to the destination folder. - /// - /// - /// Asynchronously copies the specified messages to the destination folder. - /// - /// An asynchronous task context. - /// The indexes of the messages to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task CopyToAsync (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Move the specified message to the destination folder. - /// - /// - /// Moves the specified message to the destination folder. - /// - /// The index of the message to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// does not refer to a valid message index. - /// - /// - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void MoveTo (int index, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - MoveTo (new [] { index }, destination, cancellationToken); - } - - /// - /// Asynchronously move the specified message to the destination folder. - /// - /// - /// Asynchronously moves the specified message to the destination folder. - /// - /// An asynchronous task context. - /// The index of the message to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// does not refer to a valid message index. - /// - /// - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task MoveToAsync (int index, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - return MoveToAsync (new [] { index }, destination, cancellationToken); - } - - /// - /// Move the specified messages to the destination folder. - /// - /// - /// Moves the specified messages to the destination folder. - /// - /// The indexes of the messages to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void MoveTo (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously move the specified messages to the destination folder. - /// - /// - /// Asynchronously moves the specified messages to the destination folder. - /// - /// An asynchronous task context. - /// The indexes of the messages to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task MoveToAsync (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// - /// - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList uids, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList uids, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the mail store supports quick resynchronization and the application has - /// enabled this feature via , - /// then this method will emit events for messages that - /// have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList indexes, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList indexes, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes that have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes that have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the specified message - /// indexes that have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// -or- - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (int min, int max, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// -or- - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (int min, int max, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// -or- - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes - /// (inclusive) that have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes (inclusive) that have a higher mod-sequence value than the - /// one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// -or- - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes - /// (inclusive) that have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes (inclusive) that have a higher mod-sequence value than the - /// one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Fetch the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously fetch the message summaries for the messages between the two indexes - /// (inclusive) that have a higher mod-sequence value than the one specified. - /// - /// - /// Asynchronously fetches the message summaries for the messages between - /// the two indexes (inclusive) that have a higher mod-sequence value than the - /// one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the mail service may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the specified message headers. - /// - /// - /// Gets the specified message headers. - /// - /// The message headers. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified message headers. - /// - /// - /// Asynchronously gets the specified message headers. - /// - /// The message headers. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Asynchronously gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified message headers. - /// - /// - /// Gets the specified message headers. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract HeaderList GetHeaders (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified message headers. - /// - /// - /// Asynchronously gets the specified message headers. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetHeadersAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Asynchronously gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified message. - /// - /// - /// Gets the specified message. - /// - /// The message. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified message. - /// - /// - /// Asynchronously gets the specified message. - /// - /// The message. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified message. - /// - /// - /// Gets the specified message. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract MimeMessage GetMessage (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified message. - /// - /// - /// Asynchronously gets the specified message. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// - /// - /// - /// The body part. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Asynchronously gets the specified body part. - /// - /// The body part. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Asynchronously gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified body part. - /// - /// - /// Gets a substream of the body part. If the starting offset is beyond - /// the end of the body part, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the body part, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The desired body part. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Stream GetStream (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (uid.Id == 0) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - if (offset < 0) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException (nameof (count)); - - return GetStream (uid, part.PartSpecifier, offset, count, cancellationToken, progress); - } - - /// - /// Asynchronously get a substream of the specified body part. - /// - /// - /// Asynchronously gets a substream of the body part. If the starting offset is beyond - /// the end of the body part, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the body part, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The desired body part. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task GetStreamAsync (UniqueId uid, BodyPart part, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - if (offset < 0) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException (nameof (count)); - - return GetStreamAsync (uid, part.PartSpecifier, offset, count, cancellationToken, progress); - } - - /// - /// Get a substream of the specified body part. - /// - /// - /// Gets a substream of the body part. If the starting offset is beyond - /// the end of the body part, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the body part, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The desired body part. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Stream GetStream (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - if (offset < 0) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException (nameof (count)); - - return GetStream (index, part.PartSpecifier, offset, count, cancellationToken, progress); - } - - /// - /// Asynchronously get a substream of the specified body part. - /// - /// - /// Asynchronously gets a substream of the body part. If the starting offset is beyond - /// the end of the body part, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the body part, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The desired body part. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task GetStreamAsync (int index, BodyPart part, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - if (offset < 0) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException (nameof (count)); - - return GetStreamAsync (index, part.PartSpecifier, offset, count, cancellationToken, progress); - } - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. If the starting offset is beyond - /// the end of the specified section of the message, an empty stream is returned. If - /// the number of bytes desired extends beyond the end of the section, a truncated - /// stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified message. - /// - /// - /// Asynchronously gets a substream of the specified message. If the starting - /// offset is beyond the end of the specified section of the message, an empty stream - /// is returned. If the number of bytes desired extends beyond the end of the section, - /// a truncated stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Stream GetStream (int index, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified body part. - /// - /// - /// Asynchronously gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. If the starting offset is beyond - /// the end of the specified section of the message, an empty stream is returned. If - /// the number of bytes desired extends beyond the end of the section, a truncated - /// stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get a substream of the specified body part. - /// - /// - /// Asynchronously gets a substream of the specified message. If the starting - /// offset is beyond the end of the specified section of the message, an empty stream - /// is returned. If the number of bytes desired extends beyond the end of the section, - /// a truncated stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Add a set of flags to the specified message. - /// - /// - /// Adds a set of flags to the specified message. - /// - /// The UID of the message. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void AddFlags (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - AddFlags (new [] { uid }, flags, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of flags to the specified message. - /// - /// - /// Asynchronously adds a set of flags to the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AddFlagsAsync (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlagsAsync (new [] { uid }, flags, silent, cancellationToken); - } - - /// - /// Add a set of flags to the specified message. - /// - /// - /// Adds a set of flags to the specified message. - /// - /// The UID of the message. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void AddFlags (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - AddFlags (new [] { uid }, flags, keywords, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of flags to the specified message. - /// - /// - /// Asynchronously adds a set of flags to the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AddFlagsAsync (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlagsAsync (new [] { uid }, flags, keywords, silent, cancellationToken); - } - - /// - /// Add a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void AddFlags (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - AddFlags (uids, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of flags to the specified messages. - /// - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AddFlagsAsync (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlagsAsync (uids, flags, null, silent, cancellationToken); - } - - /// - /// Add a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void AddFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages. - /// - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task AddFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified message. - /// - /// - /// Removes a set of flags from the specified message. - /// - /// The UIDs of the message. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void RemoveFlags (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveFlags (new [] { uid }, flags, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of flags from the specified message. - /// - /// - /// Asynchronously removes a set of flags from the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task RemoveFlagsAsync (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlagsAsync (new [] { uid }, flags, silent, cancellationToken); - } - - /// - /// Remove a set of flags from the specified message. - /// - /// - /// Removes a set of flags from the specified message. - /// - /// The UIDs of the message. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void RemoveFlags (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveFlags (new [] { uid }, flags, keywords, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of flags from the specified message. - /// - /// - /// Asynchronously removes a set of flags from the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task RemoveFlagsAsync (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlagsAsync (new [] { uid }, flags, keywords, silent, cancellationToken); - } - - /// - /// Remove a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void RemoveFlags (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveFlags (uids, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of flags from the specified messages. - /// - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task RemoveFlagsAsync (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlagsAsync (uids, flags, null, silent, cancellationToken); - } - - /// - /// Remove a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void RemoveFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages. - /// - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task RemoveFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified message. - /// - /// - /// Sets the flags of the specified message. - /// - /// The UIDs of the message. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void SetFlags (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - SetFlags (new [] { uid }, flags, silent, cancellationToken); - } - - /// - /// Asynchronously set the flags of the specified message. - /// - /// - /// Asynchronously sets the flags of the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SetFlagsAsync (UniqueId uid, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlagsAsync (new [] { uid }, flags, silent, cancellationToken); - } - - /// - /// Set the flags of the specified message. - /// - /// - /// Sets the flags of the specified message. - /// - /// The UIDs of the message. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void SetFlags (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - SetFlags (new [] { uid }, flags, keywords, silent, cancellationToken); - } - - /// - /// Asynchronously set the flags of the specified message. - /// - /// - /// Asynchronously sets the flags of the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SetFlagsAsync (UniqueId uid, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlagsAsync (new [] { uid }, flags, keywords, silent, cancellationToken); - } - - /// - /// Set the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void SetFlags (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - SetFlags (uids, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously set the flags of the specified messages. - /// - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SetFlagsAsync (IList uids, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlagsAsync (uids, flags, null, silent, cancellationToken); - } - - /// - /// Set the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void SetFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages. - /// - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SetFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList AddFlags (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlags (uids, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> AddFlagsAsync (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlagsAsync (uids, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList AddFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> AddFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList RemoveFlags (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlags (uids, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> RemoveFlagsAsync (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlagsAsync (uids, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList RemoveFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> RemoveFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList SetFlags (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlags (uids, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> SetFlagsAsync (IList uids, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlagsAsync (uids, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList SetFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> SetFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified message. - /// - /// - /// Adds a set of flags to the specified message. - /// - /// The index of the message. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void AddFlags (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - AddFlags (new [] { index }, flags, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of flags to the specified message. - /// - /// - /// Asynchronously adds a set of flags to the specified message. - /// - /// An asynchronous task context. - /// The index of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AddFlagsAsync (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlagsAsync (new [] { index }, flags, silent, cancellationToken); - } - - /// - /// Add a set of flags to the specified message. - /// - /// - /// Adds a set of flags to the specified message. - /// - /// The index of the message. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void AddFlags (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - AddFlags (new [] { index }, flags, keywords, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of flags to the specified message. - /// - /// - /// Asynchronously adds a set of flags to the specified message. - /// - /// An asynchronous task context. - /// The index of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AddFlagsAsync (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlagsAsync (new [] { index }, flags, keywords, silent, cancellationToken); - } - - /// - /// Add a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The indexes of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void AddFlags (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - AddFlags (indexes, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of flags to the specified messages. - /// - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AddFlagsAsync (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlagsAsync (indexes, flags, null, silent, cancellationToken); - } - - /// - /// Add a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The indexes of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void AddFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages. - /// - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task AddFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified message. - /// - /// - /// Removes a set of flags from the specified message. - /// - /// The index of the message. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void RemoveFlags (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveFlags (new [] { index }, flags, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of flags from the specified message. - /// - /// - /// Asynchronously removes a set of flags from the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task RemoveFlagsAsync (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlagsAsync (new [] { index }, flags, silent, cancellationToken); - } - - /// - /// Remove a set of flags from the specified message. - /// - /// - /// Removes a set of flags from the specified message. - /// - /// The index of the message. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void RemoveFlags (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveFlags (new [] { index }, flags, keywords, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of flags from the specified message. - /// - /// - /// Asynchronously removes a set of flags from the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task RemoveFlagsAsync (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlagsAsync (new [] { index }, flags, keywords, silent, cancellationToken); - } - - /// - /// Remove a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The indexes of the messages. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void RemoveFlags (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveFlags (indexes, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of flags from the specified messages. - /// - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task RemoveFlagsAsync (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlagsAsync (indexes, flags, null, silent, cancellationToken); - } - - /// - /// Remove a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The indexes of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void RemoveFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages. - /// - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task RemoveFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified message. - /// - /// - /// Sets the flags of the specified message. - /// - /// The index of the message. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void SetFlags (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - SetFlags (new [] { index }, flags, silent, cancellationToken); - } - - /// - /// Asynchronously set the flags of the specified message. - /// - /// - /// Asynchronously sets the flags of the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SetFlagsAsync (int index, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlagsAsync (new [] { index }, flags, silent, cancellationToken); - } - - /// - /// Set the flags of the specified message. - /// - /// - /// Sets the flags of the specified message. - /// - /// The index of the message. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void SetFlags (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - SetFlags (new [] { index }, flags, keywords, silent, cancellationToken); - } - - /// - /// Asynchronously set the flags of the specified message. - /// - /// - /// Asynchronously sets the flags of the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SetFlagsAsync (int index, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlagsAsync (new [] { index }, flags, keywords, silent, cancellationToken); - } - - /// - /// Set the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The indexes of the messages. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void SetFlags (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - SetFlags (indexes, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously set the flags of the specified messages. - /// - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SetFlagsAsync (IList indexes, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlagsAsync (indexes, flags, null, silent, cancellationToken); - } - - /// - /// Set the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The indexes of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void SetFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages. - /// - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SetFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList AddFlags (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlags (indexes, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> AddFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddFlagsAsync (indexes, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList AddFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> AddFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList RemoveFlags (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlags (indexes, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> RemoveFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveFlagsAsync (indexes, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList RemoveFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> RemoveFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList SetFlags (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlags (indexes, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Asynchronously set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> SetFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetFlagsAsync (indexes, modseq, flags, null, silent, cancellationToken); - } - - /// - /// Set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList SetFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> SetFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified message. - /// - /// - /// Adds a set of labels to the specified message. - /// - /// The UID of the message. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void AddLabels (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - AddLabels (new [] { uid }, labels, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of labels to the specified message. - /// - /// - /// Asynchronously adds a set of labels to the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AddLabelsAsync (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddLabelsAsync (new [] { uid }, labels, silent, cancellationToken); - } - - /// - /// Add a set of labels to the specified messages. - /// - /// - /// Adds a set of labels to the specified messages. - /// - /// The UIDs of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void AddLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified messages. - /// - /// - /// Asynchronously adds a set of labels to the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task AddLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified message. - /// - /// - /// Removes a set of labels from the specified message. - /// - /// The UIDs of the message. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void RemoveLabels (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveLabels (new [] { uid }, labels, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of labels from the specified message. - /// - /// - /// Asynchronously removes a set of labels from the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task RemoveLabelsAsync (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveLabelsAsync (new [] { uid }, labels, silent, cancellationToken); - } - - /// - /// Remove a set of labels from the specified messages. - /// - /// - /// Removes a set of labels from the specified messages. - /// - /// The UIDs of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void RemoveLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified messages. - /// - /// - /// Asynchronously removes a set of labels from the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task RemoveLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified message. - /// - /// - /// Sets the labels of the specified message. - /// - /// The UIDs of the message. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void SetLabels (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - SetLabels (new [] { uid }, labels, silent, cancellationToken); - } - - /// - /// Asynchronously set the labels of the specified message. - /// - /// - /// Asynchronously sets the labels of the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SetLabelsAsync (UniqueId uid, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetLabelsAsync (new [] { uid }, labels, silent, cancellationToken); - } - - /// - /// Set the labels of the specified messages. - /// - /// - /// Sets the labels of the specified messages. - /// - /// The UIDs of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void SetLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified messages. - /// - /// - /// Asynchronously sets the labels of the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SetLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList AddLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> AddLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList RemoveLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> RemoveLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList SetLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> SetLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified message. - /// - /// - /// Adds a set of labels to the specified message. - /// - /// The index of the message. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void AddLabels (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - AddLabels (new [] { index }, labels, silent, cancellationToken); - } - - /// - /// Asynchronously add a set of labels to the specified message. - /// - /// - /// Asynchronously adds a set of labels to the specified message. - /// - /// An asynchronous task context. - /// The index of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task AddLabelsAsync (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return AddLabelsAsync (new [] { index }, labels, silent, cancellationToken); - } - - /// - /// Add a set of labels to the specified messages. - /// - /// - /// Adds a set of labels to the specified messages. - /// - /// The indexes of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void AddLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified messages. - /// - /// - /// Asynchronously adds a set of labels to the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task AddLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified message. - /// - /// - /// Removes a set of labels from the specified message. - /// - /// The index of the message. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void RemoveLabels (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveLabels (new [] { index }, labels, silent, cancellationToken); - } - - /// - /// Asynchronously remove a set of labels from the specified message. - /// - /// - /// Asynchronously removes a set of labels from the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task RemoveLabelsAsync (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveLabelsAsync (new [] { index }, labels, silent, cancellationToken); - } - - /// - /// Remove a set of labels from the specified messages. - /// - /// - /// Removes a set of labels from the specified messages. - /// - /// The indexes of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void RemoveLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified messages. - /// - /// - /// Asynchronously removes a set of labels from the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task RemoveLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified message. - /// - /// - /// Sets the labels of the specified message. - /// - /// The index of the message. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void SetLabels (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - SetLabels (new [] { index }, labels, silent, cancellationToken); - } - - /// - /// Asynchronously set the labels of the specified message. - /// - /// - /// Asynchronously sets the labels of the specified message. - /// - /// An asynchronous task context. - /// The index of the message. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SetLabelsAsync (int index, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetLabelsAsync (new [] { index }, labels, silent, cancellationToken); - } - - /// - /// Set the labels of the specified messages. - /// - /// - /// Sets the labels of the specified messages. - /// - /// The indexes of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void SetLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified messages. - /// - /// - /// Asynchronously sets the labels of the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SetLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList AddLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> AddLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList RemoveLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> RemoveLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList SetLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> SetLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified message. - /// - /// - /// Stores the annotations for the specified message. - /// - /// The UID of the message. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void Store (UniqueId uid, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - Store (new [] { uid }, annotations, cancellationToken); - } - - /// - /// Asynchronously store the annotations for the specified message. - /// - /// - /// Asynchronously stores the annotations for the specified message. - /// - /// An asynchronous task context. - /// The UID of the message. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task StoreAsync (UniqueId uid, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - return StoreAsync (new [] { uid }, annotations, cancellationToken); - } - - /// - /// Store the annotations for the specified messages. - /// - /// - /// Stores the annotations for the specified messages. - /// - /// The UIDs of the messages. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Store (IList uids, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified messages. - /// - /// - /// Asynchronously stores the annotations for the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task StoreAsync (IList uids, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// -or- - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Store (IList uids, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// -or- - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> StoreAsync (IList uids, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified message. - /// - /// - /// Stores the annotations for the specified message. - /// - /// The index of the message. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual void Store (int index, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - Store (new[] { index }, annotations, cancellationToken); - } - - /// - /// Asynchronously store the annotations for the specified message. - /// - /// - /// Asynchronously stores the annotations for the specified message. - /// - /// An asynchronous task context. - /// The indexes of the message. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task StoreAsync (int index, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - return StoreAsync (new [] { index }, annotations, cancellationToken); - } - - /// - /// Store the annotations for the specified messages. - /// - /// - /// Stores the annotations for the specified messages. - /// - /// The indexes of the messages. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void Store (IList indexes, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified messages. - /// - /// - /// Asynchronously stores the annotations for the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task StoreAsync (IList indexes, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// -or- - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Store (IList indexes, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously stores the annotations for the specified messages only if their mod-sequence value is less than the specified value.s - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// -or- - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> StoreAsync (IList indexes, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Search the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported by the mail store. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Search (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously search the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported by the mail store. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> SearchAsync (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return Task.Factory.StartNew (() => { - lock (SyncRoot) { - return Search (query, cancellationToken); - } - }, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default); - } - - /// - /// Search the subset of UIDs in the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs in the specified sort order. - /// The subset of UIDs - /// The search query. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// - /// - /// One or more search terms in the are not supported by the mail store. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList Search (IList uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - var uidSet = new UidSearchQuery (uids); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return Search (uidSet.And (query), cancellationToken); - } - - /// - /// Asynchronously search the subset of UIDs in the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs in the specified sort order. - /// The subset of UIDs - /// The search query. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// - /// - /// One or more search terms in the are not supported. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> SearchAsync (IList uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - var uidSet = new UidSearchQuery (uids); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return SearchAsync (uidSet.And (query), cancellationToken); - } - - /// - /// Search the folder for messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support the specified search options. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract SearchResults Search (SearchOptions options, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously search the folder for messages matching the specified query. - /// - /// - /// Asynchronously searches the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support the specified search options. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SearchAsync (SearchOptions options, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Search the subset of UIDs in the folder for messages matching the specified query. - /// - /// - /// Searches the fsubset of UIDs in the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The subset of UIDs - /// The search query. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support the specified search options. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual SearchResults Search (SearchOptions options, IList uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - var uidSet = new UidSearchQuery (uids); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return Search (options, uidSet.And (query), cancellationToken); - } - - /// - /// Asynchronously search the subset of UIDs in the folder for messages matching the specified query. - /// - /// - /// Asynchronously searches the fsubset of UIDs in the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The subset of UIDs - /// The search query. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support the specified search options. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SearchAsync (SearchOptions options, IList uids, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - var uidSet = new UidSearchQuery (uids); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return SearchAsync (options, uidSet.And (query), cancellationToken); - } - - /// - /// Sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// /// An array of matching UIDs in the specified sort order. - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support sorting search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Sort (SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support sorting search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> SortAsync (SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The subset of UIDs - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// -or- - /// is empty. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support sorting search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual IList Sort (IList uids, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)) - { - var uidSet = new UidSearchQuery (uids); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return Sort (uidSet.And (query), orderBy, cancellationToken); - } - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The subset of UIDs - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// -or- - /// is empty. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support sorting search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task> SortAsync (IList uids, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)) - { - var uidSet = new UidSearchQuery (uids); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return SortAsync (uidSet.And (query), orderBy, cancellationToken); - } - - /// - /// Sort messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support the specified search options. - /// -or- - /// The server does not support sorting search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract SearchResults Sort (SearchOptions options, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// Asynchronously searches the folder for messages matching the specified query, returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support the specified search options. - /// -or- - /// The server does not support sorting search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SortAsync (SearchOptions options, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sort messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The subset of UIDs - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// -or- - /// is empty. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support the specified search options. - /// -or- - /// The server does not support sorting search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual SearchResults Sort (SearchOptions options, IList uids, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)) - { - var uidSet = new UidSearchQuery (uids); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return Sort (options, uidSet.And (query), orderBy, cancellationToken); - } - - /// - /// Asynchronously sort messages matching the specified query, returning the search results in the specified sort order. - /// - /// - /// Asynchronously searches the folder for messages matching the specified query, - /// returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The subset of UIDs - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// -or- - /// is empty. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support the specified search options. - /// -or- - /// The server does not support sorting search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task SortAsync (SearchOptions options, IList uids, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)) - { - var uidSet = new UidSearchQuery (uids); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - return SortAsync (options, uidSet.And (query), orderBy, cancellationToken); - } - - /// - /// Thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - /// - /// is not supported. - /// - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support threading search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Thread (ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - /// - /// is not supported. - /// - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support threading search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> ThreadAsync (ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The subset of UIDs - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - /// - /// is not supported. - /// - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support threading search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract IList Thread (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The subset of UIDs - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - /// - /// is not supported. - /// - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// - /// - /// One or more search terms in the are not supported. - /// -or- - /// The server does not support threading search results. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task> ThreadAsync (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Occurs when the folder is opened. - /// - /// - /// The event is emitted when the folder is opened. - /// - public event EventHandler Opened; - - /// - /// Raise the opened event. - /// - /// - /// Raises the opened event. - /// - protected virtual void OnOpened () - { - Opened?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the folder is closed. - /// - /// - /// The event is emitted when the folder is closed. - /// - public event EventHandler Closed; - - /// - /// Raise the closed event. - /// - /// - /// Raises the closed event. - /// - internal protected virtual void OnClosed () - { - PermanentFlags = MessageFlags.None; - AcceptedFlags = MessageFlags.None; - Access = FolderAccess.None; - - AnnotationAccess = AnnotationAccess.None; - AnnotationScopes = AnnotationScope.None; - MaxAnnotationSize = 0; - - Closed?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the folder is deleted. - /// - /// - /// The event is emitted when the folder is deleted. - /// - public event EventHandler Deleted; - - /// - /// Raise the deleted event. - /// - /// - /// Raises the deleted event. - /// - protected virtual void OnDeleted () - { - Deleted?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the folder is renamed. - /// - /// - /// The event is emitted when the folder is renamed. - /// - public event EventHandler Renamed; - - /// - /// Raise the renamed event. - /// - /// - /// Raises the renamed event. - /// - /// The old name of the folder. - /// The new name of the folder. - protected virtual void OnRenamed (string oldName, string newName) - { - Renamed?.Invoke (this, new FolderRenamedEventArgs (oldName, newName)); - } - - /// - /// Notifies the folder that a parent folder has been renamed. - /// - /// - /// implementations should override this method - /// to update their state (such as updating their - /// property). - /// - protected virtual void OnParentFolderRenamed () - { - } - - void OnParentFolderRenamed (object sender, FolderRenamedEventArgs e) - { - var oldFullName = FullName; - - OnParentFolderRenamed (); - - if (FullName != oldFullName) - OnRenamed (oldFullName, FullName); - } - - /// - /// Occurs when the folder is subscribed. - /// - /// - /// The event is emitted when the folder is subscribed. - /// - public event EventHandler Subscribed; - - /// - /// Raise the subscribed event. - /// - /// - /// Raises the subscribed event. - /// - protected virtual void OnSubscribed () - { - Subscribed?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the folder is unsubscribed. - /// - /// - /// The event is emitted when the folder is unsubscribed. - /// - public event EventHandler Unsubscribed; - - /// - /// Raise the unsubscribed event. - /// - /// - /// Raises the unsubscribed event. - /// - protected virtual void OnUnsubscribed () - { - Unsubscribed?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when a message is expunged from the folder. - /// - /// - /// The event is emitted when a message is expunged from the folder. - /// - /// - /// - /// - public event EventHandler MessageExpunged; - - /// - /// Raise the message expunged event. - /// - /// - /// Raises the message expunged event. - /// - /// The message expunged event args. - protected virtual void OnMessageExpunged (MessageEventArgs args) - { - MessageExpunged?.Invoke (this, args); - } - - /// - /// Occurs when a message vanishes from the folder. - /// - /// - /// The event is emitted when messages vanish from the folder. - /// - public event EventHandler MessagesVanished; - - /// - /// Raise the messages vanished event. - /// - /// - /// Raises the messages vanished event. - /// - /// The messages vanished event args. - protected virtual void OnMessagesVanished (MessagesVanishedEventArgs args) - { - MessagesVanished?.Invoke (this, args); - } - - /// - /// Occurs when flags changed on a message. - /// - /// - /// The event is emitted when the flags for a message are changed. - /// - /// - /// - /// - public event EventHandler MessageFlagsChanged; - - /// - /// Raise the message flags changed event. - /// - /// - /// Raises the message flags changed event. - /// - /// The message flags changed event args. - protected virtual void OnMessageFlagsChanged (MessageFlagsChangedEventArgs args) - { - MessageFlagsChanged?.Invoke (this, args); - } - - /// - /// Occurs when labels changed on a message. - /// - /// - /// The event is emitted when the labels for a message are changed. - /// - public event EventHandler MessageLabelsChanged; - - /// - /// Raise the message labels changed event. - /// - /// - /// Raises the message labels changed event. - /// - /// The message labels changed event args. - protected virtual void OnMessageLabelsChanged (MessageLabelsChangedEventArgs args) - { - MessageLabelsChanged?.Invoke (this, args); - } - - /// - /// Occurs when annotations changed on a message. - /// - /// - /// The event is emitted when the annotations for a message are changed. - /// - public event EventHandler AnnotationsChanged; - - /// - /// Raise the message annotations changed event. - /// - /// - /// Raises the message annotations changed event. - /// - /// The message annotations changed event args. - protected virtual void OnAnnotationsChanged (AnnotationsChangedEventArgs args) - { - AnnotationsChanged?.Invoke (this, args); - } - - /// - /// Occurs when a message summary is fetched from the folder. - /// - /// - /// Emitted when a message summary is fetched from the folder. - /// When multiple message summaries are being fetched from a remote folder, - /// it is possible that the connection will drop or some other exception will - /// occur, causing the Fetch method to fail and lose all of the data that has been - /// downloaded up to that point, requiring the client to request the same set of - /// message summaries all over again after it reconnects. This is obviously - /// inefficient. To alleviate this potential problem, this event will be emitted - /// as soon as the successfully parses each untagged FETCH - /// response from the server, allowing the client to commit this data immediately to - /// its local cache. - /// Depending on the IMAP server, it is possible that the - /// event will be emitted for the same message - /// multiple times if the IMAP server happens to split the requested fields into - /// multiple untagged FETCH responses. Use the - /// property to determine which f properties have - /// been populated. - /// - public event EventHandler MessageSummaryFetched; - - /// - /// Raise the message summary fetched event. - /// - /// - /// Raises the message summary fetched event. - /// When multiple message summaries are being fetched from a remote folder, - /// it is possible that the connection will drop or some other exception will - /// occur, causing the Fetch method to fail and lose all of the data that has been - /// downloaded up to that point, requiring the client to request the same set of - /// message summaries all over again after it reconnects. This is obviously - /// inefficient. To alleviate this potential problem, this event will be emitted - /// as soon as the successfully parses each untagged FETCH - /// response from the server, allowing the client to commit this data immediately to - /// its local cache. - /// Depending on the IMAP server, it is possible that - /// will be invoked for the same message - /// multiple times if the IMAP server happens to split the requested fields into - /// multiple untagged FETCH responses. Use the - /// property to determine which f properties have - /// been populated. - /// - /// The message summary. - protected virtual void OnMessageSummaryFetched (IMessageSummary message) - { - MessageSummaryFetched?.Invoke (this, new MessageSummaryFetchedEventArgs (message)); - } - - /// - /// Occurs when metadata changes. - /// - /// - /// The event is emitted when metadata changes. - /// - public event EventHandler MetadataChanged; - - /// - /// Raise the metadata changed event. - /// - /// - /// Raises the metadata changed event. - /// - /// The metadata that changed. - internal protected virtual void OnMetadataChanged (Metadata metadata) - { - MetadataChanged?.Invoke (this, new MetadataChangedEventArgs (metadata)); - } - - /// - /// Occurs when the mod-sequence changed on a message. - /// - /// - /// The event is emitted when the mod-sequence for a message is changed. - /// - public event EventHandler ModSeqChanged; - - /// - /// Raise the message mod-sequence changed event. - /// - /// - /// Raises the message mod-sequence changed event. - /// - /// The mod-sequence changed event args. - protected virtual void OnModSeqChanged (ModSeqChangedEventArgs args) - { - ModSeqChanged?.Invoke (this, args); - } - - /// - /// Occurs when the highest mod-sequence changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - public event EventHandler HighestModSeqChanged; - - /// - /// Raise the highest mod-sequence changed event. - /// - /// - /// Raises the highest mod-sequence changed event. - /// - protected virtual void OnHighestModSeqChanged () - { - HighestModSeqChanged?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the next UID changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - public event EventHandler UidNextChanged; - - /// - /// Raise the next UID changed event. - /// - /// - /// Raises the next UID changed event. - /// - protected virtual void OnUidNextChanged () - { - UidNextChanged?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the UID validity changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - public event EventHandler UidValidityChanged; - - /// - /// Raise the uid validity changed event. - /// - /// - /// Raises the uid validity changed event. - /// - protected virtual void OnUidValidityChanged () - { - UidValidityChanged?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the folder ID changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - public event EventHandler IdChanged; - - /// - /// Raise the ID changed event. - /// - /// - /// Raises the ID changed event. - /// - protected virtual void OnIdChanged () - { - IdChanged?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the folder size changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - public event EventHandler SizeChanged; - - /// - /// Raise the size changed event. - /// - /// - /// Raises the size changed event. - /// - protected virtual void OnSizeChanged () - { - SizeChanged?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the message count changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - /// - /// - /// - public event EventHandler CountChanged; - - /// - /// Raise the message count changed event. - /// - /// - /// Raises the message count changed event. - /// - protected virtual void OnCountChanged () - { - CountChanged?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the recent message count changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - public event EventHandler RecentChanged; - - /// - /// Raise the recent message count changed event. - /// - /// - /// Raises the recent message count changed event. - /// - protected virtual void OnRecentChanged () - { - RecentChanged?.Invoke (this, EventArgs.Empty); - } - - /// - /// Occurs when the unread message count changes. - /// - /// - /// The event is emitted whenever the value changes. - /// - public event EventHandler UnreadChanged; - - /// - /// Raise the unread message count changed event. - /// - /// - /// Raises the unread message count changed event. - /// - protected virtual void OnUnreadChanged () - { - UnreadChanged?.Invoke (this, EventArgs.Empty); - } - - #region IEnumerable implementation - - /// - /// Get an enumerator for the messages in the folder. - /// - /// - /// Gets an enumerator for the messages in the folder. - /// - /// The enumerator. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - public abstract IEnumerator GetEnumerator (); - - /// - /// Get an enumerator for the messages in the folder. - /// - /// - /// Gets an enumerator for the messages in the folder. - /// - /// The enumerator. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder is not currently open. - /// - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - return FullName; - } - } -} diff --git a/src/MailKit/MailService.cs b/src/MailKit/MailService.cs deleted file mode 100644 index 0dd1673..0000000 --- a/src/MailKit/MailService.cs +++ /dev/null @@ -1,1582 +0,0 @@ -// -// MailService.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Net.Security; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; -using SslProtocols = System.Security.Authentication.SslProtocols; - -using MailKit.Net; -using MailKit.Net.Proxy; -using MailKit.Security; - -namespace MailKit { - /// - /// An abstract mail service implementation. - /// - /// - /// An abstract mail service implementation. - /// - public abstract class MailService : IMailService - { -#if NET48 - const SslProtocols DefaultSslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13; -#else - const SslProtocols DefaultSslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12; -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The protocol logger. - /// - /// is null. - /// - protected MailService (IProtocolLogger protocolLogger) - { - if (protocolLogger == null) - throw new ArgumentNullException (nameof (protocolLogger)); - - SslProtocols = DefaultSslProtocols; - CheckCertificateRevocation = true; - ProtocolLogger = protocolLogger; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - protected MailService () : this (new NullProtocolLogger ()) - { - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~MailService () - { - Dispose (false); - } - - /// - /// Gets an object that can be used to synchronize access to the service. - /// - /// - /// Gets an object that can be used to synchronize access to the service. - /// - /// The sync root. - public abstract object SyncRoot { - get; - } - - /// - /// Gets the protocol supported by the message service. - /// - /// - /// Gets the protocol supported by the message service. - /// - /// The protocol. - protected abstract string Protocol { - get; - } - - /// - /// Get the protocol logger. - /// - /// - /// Gets the protocol logger. - /// - /// The protocol logger. - public IProtocolLogger ProtocolLogger { - get; private set; - } - - /// - /// Gets or sets the SSL and TLS protocol versions that the client is allowed to use. - /// - /// - /// Gets or sets the SSL and TLS protocol versions that the client is allowed to use. - /// By default, MailKit initializes this value to support only TLS v1.1 and greater and - /// does not support TLS v1.0 or any version of SSL due to those protocols no longer being considered - /// secure. - /// This property should be set before calling any of the - /// Connect methods. - /// - /// The SSL and TLS protocol versions that are supported. - public SslProtocols SslProtocols { - get; set; - } - - /// - /// Gets or sets the client SSL certificates. - /// - /// - /// Some servers may require the client SSL certificates in order - /// to allow the user to connect. - /// This property should be set before calling any of the - /// Connect methods. - /// - /// The client SSL certificates. - public X509CertificateCollection ClientCertificates { - get; set; - } - - /// - /// Get or set whether connecting via SSL/TLS should check certificate revocation. - /// - /// - /// Gets or sets whether connecting via SSL/TLS should check certificate revocation. - /// Normally, the value of this property should be set to true (the default) for security - /// reasons, but there are times when it may be necessary to set it to false. - /// For example, most Certificate Authorities are probably pretty good at keeping their CRL and/or - /// OCSP servers up 24/7, but occasionally they do go down or are otherwise unreachable due to other - /// network problems between the client and the Certificate Authority. When this happens, it becomes - /// impossible to check the revocation status of one or more of the certificates in the chain - /// resulting in an being thrown in the - /// Connect method. If this becomes a problem, - /// it may become desirable to set to false. - /// - /// true if certificate revocation should be checked; otherwise, false. - public bool CheckCertificateRevocation { - get; set; - } - - /// - /// Get or sets a callback function to validate the server certificate. - /// - /// - /// Gets or sets a callback function to validate the server certificate. - /// This property should be set before calling any of the - /// Connect methods. - /// - /// - /// - /// - /// The server certificate validation callback function. - public RemoteCertificateValidationCallback ServerCertificateValidationCallback { - get; set; - } - - /// - /// Get or set the local IP end point to use when connecting to the remote host. - /// - /// - /// Gets or sets the local IP end point to use when connecting to the remote host. - /// - /// The local IP end point or null to use the default end point. - public IPEndPoint LocalEndPoint { - get; set; - } - - /// - /// Get or set the proxy client to use when connecting to a remote host. - /// - /// - /// Gets or sets the proxy client to use when connecting to a remote host via any of the - /// Connect methods. - /// - /// The proxy client. - public IProxyClient ProxyClient { - get; set; - } - - /// - /// Gets the authentication mechanisms supported by the mail server. - /// - /// - /// The authentication mechanisms are queried as part of the - /// Connect method. - /// - /// The authentication mechanisms. - public abstract HashSet AuthenticationMechanisms { - get; - } - - /// - /// Gets whether or not the client is currently connected to an mail server. - /// - /// - ///The state is set to true immediately after - /// one of the Connect - /// methods succeeds and is not set back to false until either the client - /// is disconnected via or until a - /// is thrown while attempting to read or write to - /// the underlying network socket. - /// When an is caught, the connection state of the - /// should be checked before continuing. - /// - /// true if the client is connected; otherwise, false. - public abstract bool IsConnected { - get; - } - - /// - /// 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). - /// - /// true if the connection is secure; otherwise, false. - public abstract bool IsSecure { - get; - } - - /// - /// Get whether or not the client is currently authenticated with the mail server. - /// - /// - /// Gets whether or not the client is currently authenticated with the mail server. - /// To authenticate with the mail server, use one of the - /// Authenticate methods - /// or any of the Async alternatives. - /// - /// true if the client is authenticated; otherwise, false. - public abstract bool IsAuthenticated { - get; - } - - /// - /// Gets or sets the timeout for network streaming operations, in milliseconds. - /// - /// - /// Gets or sets the underlying socket stream's - /// and values. - /// - /// The timeout in milliseconds. - public abstract int Timeout { - get; set; - } - - const string AppleCertificateIssuer = "C=US, O=Apple Inc., OU=Certification Authority, CN=Apple IST CA 2 - G1"; - const string GMailCertificateIssuer = "CN=GTS CA 1O1, O=Google Trust Services, C=US"; - const string OutlookCertificateIssuer = "CN=DigiCert Cloud Services CA-1, O=DigiCert Inc, C=US"; - const string YahooCertificateIssuer = "CN=DigiCert SHA2 High Assurance Server CA, OU=www.digicert.com, O=DigiCert Inc, C=US"; - const string GmxCertificateIssuer = "CN=TeleSec ServerPass Extended Validation Class 3 CA, STREET=Untere Industriestr. 20, L=Netphen, OID.2.5.4.17=57250, S=Nordrhein Westfalen, OU=T-Systems Trust Center, O=T-Systems International GmbH, C=DE"; - - static bool IsKnownMailServerCertificate (X509Certificate2 certificate) - { - var cn = certificate.GetNameInfo (X509NameType.SimpleName, false); - var fingerprint = certificate.Thumbprint; - var serial = certificate.SerialNumber; - var issuer = certificate.Issuer; - - switch (cn) { - case "imap.gmail.com": - return issuer == GMailCertificateIssuer && serial == "0096768414983DDE9C0800000000320A68" && fingerprint == "A53BA86C137D828618540738014F7C3D52F699C7"; - case "pop.gmail.com": - return issuer == GMailCertificateIssuer && serial == "00D80446EA4406BA970800000000320A6A" && fingerprint == "379A18659C855AE5CD00E24CEBE2C6552235B701"; - case "smtp.gmail.com": - return issuer == GMailCertificateIssuer && serial == "00A2683EEFC8500CA20800000000320A71" && fingerprint == "8F0A0B43DE223D360C4BBC41725C202B806CED32"; - case "outlook.com": - return issuer == OutlookCertificateIssuer && serial == "0654F84B6325595A20BC68A6A5851CBB" && fingerprint == "7F0804B4D0A6C83E46A3A00EC98F8343D7308566"; - case "imap.mail.me.com": - return issuer == AppleCertificateIssuer && serial == "62CBBFC566127C4758E96BDBC38EC9E6" && fingerprint == "E1A5F9D22A810979CACDFC0B4151F561E8D02976"; - case "smtp.mail.me.com": - return issuer == AppleCertificateIssuer && serial == "3460D64A763D9ACA4B460C25021653C7" && fingerprint == "C262F01E83D6CE0C361E8B049E5BE8FE6E55806B"; - case "*.imap.mail.yahoo.com": - return issuer == YahooCertificateIssuer && serial == "0B2804C9ED82D14FEFEF111E54A0551C" && fingerprint == "F8047F0F60C4641F718353BE7DDC31665B96B5C0"; - case "legacy.pop.mail.yahoo.com": - return issuer == YahooCertificateIssuer && serial == "05179AA3E07FA5B4D0FC55A7A950B8D8" && fingerprint == "08E010CBAEFAADD20DB0B222C8B6812E762F28EC"; - case "smtp.mail.yahoo.com": - return issuer == YahooCertificateIssuer && serial == "0F962C48837807B6556C5B6961FC4671" && fingerprint == "E53995EBA816FB73FD4F4BD55ABED04981DA0F18"; - case "mail.gmx.net": - return issuer == GmxCertificateIssuer && serial == "218296213149726650EB233346353EEA" && fingerprint == "67DED57393303E005937D5EDECB6A29C136024CA"; - default: - return false; - } - } - - static bool IsUntrustedRoot (X509Chain chain) - { - foreach (var status in chain.ChainStatus) { - if (status.Status == X509ChainStatusFlags.NoError || status.Status == X509ChainStatusFlags.UntrustedRoot) - continue; - - return false; - } - - return true; - } - - internal SslCertificateValidationInfo SslCertificateValidationInfo; - - /// - /// The default server certificate validation callback used when connecting via SSL or TLS. - /// - /// - /// The default server certificate validation callback recognizes and accepts the certificates - /// for a list of commonly used mail servers such as gmail.com, outlook.com, mail.me.com, yahoo.com, - /// and gmx.net. - /// - /// true if the certificate is deemed valid; otherwise, false. - /// The object that is connecting via SSL or TLS. - /// The server's SSL certificate. - /// The server's SSL certificate chain. - /// The SSL policy errors. - protected bool DefaultServerCertificateValidationCallback (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { - const SslPolicyErrors mask = SslPolicyErrors.RemoteCertificateNotAvailable | SslPolicyErrors.RemoteCertificateNameMismatch; - - SslCertificateValidationInfo = null; - - if (sslPolicyErrors == SslPolicyErrors.None) - return true; - - if ((sslPolicyErrors & mask) == 0) { - // At this point, all that is left is SslPolicyErrors.RemoteCertificateChainErrors - - // If the problem is an untrusted root, then compare the certificate to a list of known mail server certificates. - if (IsUntrustedRoot (chain) && certificate is X509Certificate2 certificate2) { - if (IsKnownMailServerCertificate (certificate2)) - return true; - } - } - - SslCertificateValidationInfo = new SslCertificateValidationInfo (sender, certificate, chain, sslPolicyErrors); - - return false; - } - - internal async Task ConnectSocket (string host, int port, bool doAsync, CancellationToken cancellationToken) - { - if (ProxyClient != null) { - ProxyClient.LocalEndPoint = LocalEndPoint; - - if (doAsync) - return await ProxyClient.ConnectAsync (host, port, Timeout, cancellationToken).ConfigureAwait (false); - - return ProxyClient.Connect (host, port, Timeout, cancellationToken); - } - - return await SocketUtils.ConnectAsync (host, port, LocalEndPoint, Timeout, doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Establish a connection to the specified mail server. - /// - /// - /// Establishes a connection to the specified mail server. - /// - /// - /// - /// - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public abstract void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously establish a connection to the specified mail server. - /// - /// - /// Asynchronously establishes a connection to the specified mail server. - /// - /// An asynchronous task context. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public abstract Task ConnectAsync (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Establish a connection to the specified mail server using the provided socket. - /// - /// - /// Establish a connection to the specified mail server using the provided socket. - /// If a successful connection is made, the - /// property will be populated. - /// - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - public abstract void Connect (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously establish a connection to the specified mail server using the provided socket. - /// - /// - /// Asynchronously establishes a connection to the specified mail server using the provided socket. - /// If a successful connection is made, the - /// property will be populated. - /// - /// An asynchronous task context. - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - public abstract Task ConnectAsync (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Establish a connection to the specified mail server using the provided stream. - /// - /// - /// Establish a connection to the specified mail server using the provided stream. - /// If a successful connection is made, the - /// property will be populated. - /// - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - public abstract void Connect (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously establish a connection to the specified mail server using the provided stream. - /// - /// - /// Asynchronously establishes a connection to the specified mail server using the provided stream. - /// If a successful connection is made, the - /// property will be populated. - /// - /// An asynchronous task context. - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - public abstract Task ConnectAsync (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)); - - internal SecureSocketOptions GetSecureSocketOptions (Uri uri) - { - var query = uri.ParsedQuery (); - var protocol = uri.Scheme; - string value; - - // Note: early versions of MailKit used "pop3" and "pop3s" - if (protocol.Equals ("pop3s", StringComparison.OrdinalIgnoreCase)) - protocol = "pops"; - else if (protocol.Equals ("pop3", StringComparison.OrdinalIgnoreCase)) - protocol = "pop"; - - if (protocol.Equals (Protocol + "s", StringComparison.OrdinalIgnoreCase)) - return SecureSocketOptions.SslOnConnect; - - if (!protocol.Equals (Protocol, StringComparison.OrdinalIgnoreCase)) - throw new ArgumentException ("Unknown URI scheme.", nameof (uri)); - - if (query.TryGetValue ("starttls", out value)) { - switch (value.ToLowerInvariant ()) { - default: - return SecureSocketOptions.StartTlsWhenAvailable; - case "always": case "true": case "yes": - return SecureSocketOptions.StartTls; - case "never": case "false": case "no": - return SecureSocketOptions.None; - } - } - - return SecureSocketOptions.StartTlsWhenAvailable; - } - - /// - /// Establish a connection to the specified mail server. - /// - /// - /// Establishes a connection to the specified mail server. - /// - /// - /// - /// - /// The server URI. - /// The cancellation token. - /// - /// The is null. - /// - /// - /// The is not an absolute URI. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public void Connect (Uri uri, CancellationToken cancellationToken = default (CancellationToken)) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - if (!uri.IsAbsoluteUri) - throw new ArgumentException ("The uri must be absolute.", nameof (uri)); - - var options = GetSecureSocketOptions (uri); - - Connect (uri.Host, uri.Port < 0 ? 0 : uri.Port, options, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified mail server. - /// - /// - /// Asynchronously establishes a connection to the specified mail server. - /// - /// An asynchronous task context. - /// The server URI. - /// The cancellation token. - /// - /// The is null. - /// - /// - /// The is not an absolute URI. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public Task ConnectAsync (Uri uri, CancellationToken cancellationToken = default (CancellationToken)) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - if (!uri.IsAbsoluteUri) - throw new ArgumentException ("The uri must be absolute.", nameof (uri)); - - var options = GetSecureSocketOptions (uri); - - return ConnectAsync (uri.Host, uri.Port < 0 ? 0 : uri.Port, options, cancellationToken); - } - - /// - /// Establish a connection to the specified mail server. - /// - /// - /// Establishes a connection to the specified mail server. - /// - /// The argument only controls whether or - /// not the client makes an SSL-wrapped connection. In other words, even if the - /// parameter is false, SSL/TLS may still be used if - /// the mail server supports the STARTTLS extension. - /// To disable all use of SSL/TLS, use the - /// - /// overload with a value of - /// SecureSocketOptions.None - /// instead. - /// - /// - /// The host to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// true if the client should make an SSL-wrapped connection to the server; otherwise, false. - /// The cancellation token. - /// - /// The is null. - /// - /// - /// is out of range (0 to 65535, inclusive). - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public void Connect (string host, int port, bool useSsl, CancellationToken cancellationToken = default (CancellationToken)) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0) - throw new ArgumentException ("The host name cannot be empty.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - Connect (host, port, useSsl ? SecureSocketOptions.SslOnConnect : SecureSocketOptions.StartTlsWhenAvailable, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified mail server. - /// - /// - /// Asynchronously establishes a connection to the specified mail server. - /// - /// The argument only controls whether or - /// not the client makes an SSL-wrapped connection. In other words, even if the - /// parameter is false, SSL/TLS may still be used if - /// the mail server supports the STARTTLS extension. - /// To disable all use of SSL/TLS, use the - /// - /// overload with a value of - /// SecureSocketOptions.None - /// instead. - /// - /// - /// An asynchronous task context. - /// The host to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// true if the client should make an SSL-wrapped connection to the server; otherwise, false. - /// The cancellation token. - /// - /// The is null. - /// - /// - /// is out of range (0 to 65535, inclusive). - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public Task ConnectAsync (string host, int port, bool useSsl, CancellationToken cancellationToken = default (CancellationToken)) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0) - throw new ArgumentException ("The host name cannot be empty.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - return ConnectAsync (host, port, useSsl ? SecureSocketOptions.SslOnConnect : SecureSocketOptions.StartTlsWhenAvailable, cancellationToken); - } - - /// - /// Authenticate using the supplied credentials. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// The encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public abstract void Authenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously authenticate using the supplied credentials. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// An asynchronous task context. - /// The encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public abstract Task AuthenticateAsync (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Authenticate using the supplied credentials. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public void Authenticate (ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)) - { - Authenticate (Encoding.UTF8, credentials, cancellationToken); - } - - /// - /// Asynchronously authenticate using the supplied credentials. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// An asynchronous task context. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public Task AuthenticateAsync (ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)) - { - return AuthenticateAsync (Encoding.UTF8, credentials, cancellationToken); - } - - /// - /// Authenticate using the specified user name and password. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// The encoding to use for the user's credentials. - /// The user name. - /// The password. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public void Authenticate (Encoding encoding, string userName, string password, CancellationToken cancellationToken = default (CancellationToken)) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (userName == null) - throw new ArgumentNullException (nameof (userName)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - var credentials = new NetworkCredential (userName, password); - - Authenticate (encoding, credentials, cancellationToken); - } - - /// - /// Asynchronously authenticate using the specified user name and password. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// An asynchronous task context. - /// The encoding to use for the user's credentials. - /// The user name. - /// The password. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public Task AuthenticateAsync (Encoding encoding, string userName, string password, CancellationToken cancellationToken = default (CancellationToken)) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (userName == null) - throw new ArgumentNullException (nameof (userName)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - var credentials = new NetworkCredential (userName, password); - - return AuthenticateAsync (encoding, credentials, cancellationToken); - } - - /// - /// Authenticate using the specified user name and password. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// - /// - /// - /// The user name. - /// The password. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public void Authenticate (string userName, string password, CancellationToken cancellationToken = default (CancellationToken)) - { - Authenticate (Encoding.UTF8, userName, password, cancellationToken); - } - - /// - /// Asynchronously authenticate using the specified user name and password. - /// - /// - /// If the server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the default login command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// An asynchronous task context. - /// The user name. - /// The password. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public Task AuthenticateAsync (string userName, string password, CancellationToken cancellationToken = default (CancellationToken)) - { - return AuthenticateAsync (Encoding.UTF8, userName, password, cancellationToken); - } - - /// - /// Authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public abstract void Authenticate (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// An asynchronous task context. - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected or is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - public abstract Task AuthenticateAsync (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Disconnect the service. - /// - /// - /// If is true, a logout/quit command will be issued in order to disconnect cleanly. - /// - /// - /// - /// - /// If set to true, a logout/quit command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - public abstract void Disconnect (bool quit, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously disconnect the service. - /// - /// - /// If is true, a logout/quit command will be issued in order to disconnect cleanly. - /// - /// An asynchronous task context. - /// If set to true, a logout/quit command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - public abstract Task DisconnectAsync (bool quit, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Ping the mail server to keep the connection alive. - /// - /// - /// Mail servers, if left idle for too long, will automatically drop the connection. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// -or- - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - public abstract void NoOp (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously ping the mail server to keep the connection alive. - /// - /// - /// Mail servers, if left idle for too long, will automatically drop the connection. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// -or- - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command was rejected by the mail server. - /// - /// - /// The server responded with an unexpected token. - /// - public abstract Task NoOpAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Occurs when the client has been successfully connected. - /// - /// - /// The event is raised when the client - /// successfully connects to the mail server. - /// - public event EventHandler Connected; - - /// - /// Raise the connected event. - /// - /// - /// Raises the connected event. - /// - /// The name of the host that the client connected to. - /// The port that the client connected to on the remote host. - /// The SSL/TLS options that were used when connecting. - protected virtual void OnConnected (string host, int port, SecureSocketOptions options) - { - var handler = Connected; - - if (handler != null) - handler (this, new ConnectedEventArgs (host, port, options)); - } - - /// - /// Occurs when the client gets disconnected. - /// - /// - /// The event is raised whenever the client - /// gets disconnected. - /// - public event EventHandler Disconnected; - - /// - /// Raise the disconnected event. - /// - /// - /// Raises the disconnected event. - /// - /// The name of the host that the client was connected to. - /// The port that the client was connected to on the remote host. - /// The SSL/TLS options that were used by the client. - /// true if the disconnect was explicitly requested; otherwise, false. - protected virtual void OnDisconnected (string host, int port, SecureSocketOptions options, bool requested) - { - var handler = Disconnected; - - if (handler != null) - handler (this, new DisconnectedEventArgs (host, port, options, requested)); - } - - /// - /// Occurs when the client has been successfully authenticated. - /// - /// - /// The event is raised whenever the client - /// has been authenticated. - /// - public event EventHandler Authenticated; - - /// - /// Raise the authenticated event. - /// - /// - /// Raises the authenticated event. - /// - /// The notification sent by the server when the client successfully authenticates. - protected virtual void OnAuthenticated (string message) - { - var handler = Authenticated; - - if (handler != null) - handler (this, new AuthenticatedEventArgs (message)); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected virtual void Dispose (bool disposing) - { - if (disposing) - ProtocolLogger.Dispose (); - } - - /// - /// Releases all resource used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - } - } -} diff --git a/src/MailKit/MailSpool.cs b/src/MailKit/MailSpool.cs deleted file mode 100644 index 17c0974..0000000 --- a/src/MailKit/MailSpool.cs +++ /dev/null @@ -1,1588 +0,0 @@ -// -// MailSpool.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Collections; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -namespace MailKit { - /// - /// An abstract mail spool implementation. - /// - /// - /// An abstract mail spool implementation. - /// - public abstract class MailSpool : MailService, IMailSpool - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The protocol logger. - /// - /// is null. - /// - protected MailSpool (IProtocolLogger protocolLogger) : base (protocolLogger) - { - } - - /// - /// Get the number of messages available in the message spool. - /// - /// - /// Gets the number of messages available in the message spool. - /// Once authenticated, the property will be set - /// to the number of available messages in the spool. - /// - /// - /// - /// - /// The message count. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - public abstract int Count { - get; - } - - /// - /// Get whether or not the service supports referencing messages by UIDs. - /// - /// - /// Not all servers support referencing messages by UID, so this property should - /// be checked before using - /// and . - /// If the server does not support UIDs, then all methods that take UID arguments - /// along with and - /// will fail. - /// - /// true if supports uids; otherwise, false. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - public abstract bool SupportsUids { - get; - } - - /// - /// Get the message count. - /// - /// - /// Gets the message count. - /// - /// The message count. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract int GetMessageCount (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the message count. - /// - /// - /// Asynchronously gets the message count. - /// - /// The message count. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task GetMessageCountAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the UID of the message at the specified index. - /// - /// - /// Not all servers support UIDs, so you should first check - /// the property. - /// - /// The message UID. - /// The message index. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail spool does not support UIDs. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract string GetMessageUid (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the UID of the message at the specified index. - /// - /// - /// Not all servers support UIDs, so you should first check - /// the property. - /// - /// The message UID. - /// The message index. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail spool does not support UIDs. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task GetMessageUidAsync (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the full list of available message UIDs. - /// - /// - /// Not all servers support UIDs, so you should first check - /// the property. - /// - /// - /// - /// - /// The message uids. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail spool does not support UIDs. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IList GetMessageUids (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the full list of available message UIDs. - /// - /// - /// Not all servers support UIDs, so you should first check - /// the property. - /// - /// The message uids. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The mail spool does not support UIDs. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task> GetMessageUidsAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the size of the specified message, in bytes. - /// - /// - /// Gets the size of the specified message, in bytes. - /// - /// The message size, in bytes. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract int GetMessageSize (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the size of the specified message, in bytes. - /// - /// - /// Asynchronously gets the size of the specified message, in bytes. - /// - /// The message size, in bytes. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task GetMessageSizeAsync (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the sizes for all available messages, in bytes. - /// - /// - /// Gets the sizes for all available messages, in bytes. - /// - /// The message sizes, in bytes. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IList GetMessageSizes (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the sizes for all available messages, in bytes. - /// - /// - /// Asynchronously gets the sizes for all available messages, in bytes. - /// - /// The message sizes, in bytes. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task> GetMessageSizesAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the headers for the specified message. - /// - /// - /// Gets the headers for the specified message. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract HeaderList GetMessageHeaders (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the headers for the specified message. - /// - /// - /// Asynchronously gets the headers for the specified message. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task GetMessageHeadersAsync (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the headers for the specified messages. - /// - /// - /// Gets the headers for the specified messages. - /// - /// The headers for the specified messages. - /// The indexes of the messages. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// -or- - /// No indexes were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IList GetMessageHeaders (IList indexes, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the headers for the specified messages. - /// - /// - /// Asynchronously gets the headers for the specified messages. - /// - /// The headers for the specified messages. - /// The indexes of the messages. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// -or- - /// No indexes were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task> GetMessageHeadersAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the headers of the messages within the specified range. - /// - /// - /// Gets the headers of the messages within the specified range. - /// - /// The headers of the messages within the specified range. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IList GetMessageHeaders (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the headers of the messages within the specified range. - /// - /// - /// Gets the headers of the messages within the specified range. - /// - /// The headers of the messages within the specified range. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task> GetMessageHeadersAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the message at the specified index. - /// - /// - /// Gets the message at the specified index. - /// - /// - /// - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract MimeMessage GetMessage (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the message at the specified index. - /// - /// - /// Asynchronously gets the message at the specified index. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task GetMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the messages at the specified indexes. - /// - /// - /// Get the messages at the specified indexes. - /// - /// The messages. - /// The indexes of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// -or- - /// No indexes were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IList GetMessages (IList indexes, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the messages at the specified indexes. - /// - /// - /// Asynchronously get the messages at the specified indexes. - /// - /// The messages. - /// The indexes of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// -or- - /// No indexes were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the messages within the specified range. - /// - /// - /// Gets the messages within the specified range. - /// - /// - /// - /// - /// The messages. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the messages within the specified range. - /// - /// - /// Asynchronously gets the messages within the specified range. - /// - /// The messages. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the message or header stream at the specified index. - /// - /// - /// Gets the message or header stream at the specified index. - /// - /// The message or header stream. - /// The index of the message. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the message or header stream at the specified index. - /// - /// - /// Asynchronously gets the message or header stream at the specified index. - /// - /// The message or header stream. - /// The index of the message. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the message or header streams at the specified indexes. - /// - /// - /// Get the message or header streams at the specified indexes. - /// If the mail server supports pipelining, this method will likely be more - /// efficient than using for - /// each message because it will batch the commands to reduce latency. - /// - /// The message or header streams. - /// The indexes of the messages. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// -or- - /// No indexes were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the message or header streams at the specified indexes. - /// - /// - /// Asynchronously get the message or header streams at the specified indexes. - /// - /// The messages. - /// The indexes of the messages. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// -or- - /// No indexes were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the message or header streams within the specified range. - /// - /// - /// Gets the message or header streams within the specified range. - /// If the mail server supports pipelining, this method will likely be more - /// efficient than using for - /// each message because it will batch the commands to reduce latency. - /// - /// The message or header streams. - /// The index of the first stream to get. - /// The number of streams to get. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the message or header streams within the specified range. - /// - /// - /// Asynchronously gets the message or header streams within the specified range. - /// - /// The messages. - /// The index of the first stream to get. - /// The number of streams to get. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Mark the specified message for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// - /// - /// - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract void DeleteMessage (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously mark the specified message for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task DeleteMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Mark the specified messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The indexes of the messages. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// -or- - /// No indexes were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract void DeleteMessages (IList indexes, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously mark the specified messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// -or- - /// No indexes were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task DeleteMessagesAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Mark the specified range of messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// - /// - /// - /// The index of the first message to mark for deletion. - /// The number of messages to mark for deletion. - /// The cancellation token. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract void DeleteMessages (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously mark the specified range of messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The index of the first message to mark for deletion. - /// The number of messages to mark for deletion. - /// The cancellation token. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task DeleteMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Mark all messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract void DeleteAllMessages (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously mark all messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task DeleteAllMessagesAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Reset the state of all messages marked for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract void Reset (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously reset the state of all messages marked for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract Task ResetAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get an enumerator for the messages in the folder. - /// - /// - /// Gets an enumerator for the messages in the folder. - /// - /// The enumerator. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A command failed. - /// - /// - /// A protocol error occurred. - /// - public abstract IEnumerator GetEnumerator (); - - /// - /// Get an enumerator for the messages in the folder. - /// - /// - /// Gets an enumerator for the messages in the folder. - /// - /// The enumerator. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A command failed. - /// - /// - /// A protocol error occurred. - /// - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - } -} diff --git a/src/MailKit/MailStore.cs b/src/MailKit/MailStore.cs deleted file mode 100644 index bc8d8bf..0000000 --- a/src/MailKit/MailStore.cs +++ /dev/null @@ -1,872 +0,0 @@ -// -// MailStore.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MailKit { - /// - /// An abstract mail store implementation. - /// - /// - /// An abstract mail store implementation. - /// - public abstract class MailStore : MailService, IMailStore - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The protocol logger. - /// - /// is null. - /// - protected MailStore (IProtocolLogger protocolLogger) : base (protocolLogger) - { - } - - /// - /// Gets the personal namespaces. - /// - /// - /// The personal folder namespaces contain a user's personal mailbox folders. - /// - /// The personal namespaces. - public abstract FolderNamespaceCollection PersonalNamespaces { - get; - } - - /// - /// Gets the shared namespaces. - /// - /// - /// The shared folder namespaces contain mailbox folders that are shared with the user. - /// - /// The shared namespaces. - public abstract FolderNamespaceCollection SharedNamespaces { - get; - } - - /// - /// Gets the other namespaces. - /// - /// - /// The other folder namespaces contain other mailbox folders. - /// - /// The other namespaces. - public abstract FolderNamespaceCollection OtherNamespaces { - get; - } - - /// - /// Get whether or not the mail store supports quotas. - /// - /// - /// Gets whether or not the mail store supports quotas. - /// - /// true if the mail store supports quotas; otherwise, false. - public abstract bool SupportsQuotas { - get; - } - - /// - /// Get the threading algorithms supported by the mail store. - /// - /// - /// The threading algorithms are queried as part of the - /// Connect - /// and Authenticate methods. - /// - /// - /// - /// - /// The supported threading algorithms. - public abstract HashSet ThreadingAlgorithms { - get; - } - - /// - /// Get the Inbox folder. - /// - /// - /// The Inbox folder is the default folder and always exists on the mail store. - /// This property will only be available after the client has been authenticated. - /// - /// The Inbox folder. - public abstract IMailFolder Inbox { - get; - } - - /// - /// Enable the quick resynchronization feature. - /// - /// - /// Enables quick resynchronization when a folder is opened using the - /// - /// method. - /// If this feature is enabled, the event is replaced - /// with the event. - /// This method needs to be called immediately after calling one of the - /// Authenticate methods, before - /// opening any folders. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Quick resynchronization needs to be enabled before selecting a folder. - /// - /// - /// The mail store does not support quick resynchronization. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - public abstract void EnableQuickResync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously enable the quick resynchronization feature. - /// - /// - /// Enables quick resynchronization when a folder is opened using the - /// - /// method. - /// If this feature is enabled, the event is replaced - /// with the event. - /// This method needs to be called immediately after calling one of the - /// Authenticate methods, before - /// opening any folders. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Quick resynchronization needs to be enabled before selecting a folder. - /// - /// - /// The mail store does not support quick resynchronization. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - public abstract Task EnableQuickResyncAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the specified special folder. - /// - /// - /// Not all mail stores support special folders. Each implementation - /// should provide a way to determine if special folders are supported. - /// - /// The folder if available; otherwise null. - /// The type of special folder. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - public abstract IMailFolder GetFolder (SpecialFolder folder); - - /// - /// Get the folder for the specified namespace. - /// - /// - /// Gets the folder for the specified namespace. - /// - /// The folder. - /// The namespace. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder could not be found. - /// - public abstract IMailFolder GetFolder (FolderNamespace @namespace); - - /// - /// Get all of the folders within the specified namespace. - /// - /// - /// Gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - public virtual IList GetFolders (FolderNamespace @namespace, bool subscribedOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetFolders (@namespace, StatusItems.None, subscribedOnly, cancellationToken); - } - - /// - /// Asynchronously get all of the folders within the specified namespace. - /// - /// - /// Asynchronously gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - public virtual Task> GetFoldersAsync (FolderNamespace @namespace, bool subscribedOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetFoldersAsync (@namespace, StatusItems.None, subscribedOnly, cancellationToken); - } - - /// - /// Get all of the folders within the specified namespace. - /// - /// - /// Gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - public abstract IList GetFolders (FolderNamespace @namespace, StatusItems items = StatusItems.None, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get all of the folders within the specified namespace. - /// - /// - /// Asynchronously gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - public abstract Task> GetFoldersAsync (FolderNamespace @namespace, StatusItems items = StatusItems.None, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the folder for the specified path. - /// - /// - /// Gets the folder for the specified path. - /// - /// The folder. - /// The folder path. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - public abstract IMailFolder GetFolder (string path, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the folder for the specified path. - /// - /// - /// Asynchronously gets the folder for the specified path. - /// - /// The folder. - /// The folder path. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// A protocol error occurred. - /// - /// - /// The command failed. - /// - public abstract Task GetFolderAsync (string path, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual MetadataCollection GetMetadata (IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadata (new MetadataOptions (), tags, cancellationToken); - } - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public virtual Task GetMetadataAsync (IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (new MetadataOptions (), tags, cancellationToken); - } - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract MetadataCollection GetMetadata (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Asynchronously gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task GetMetadataAsync (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sets the specified metadata. - /// - /// - /// Sets the specified metadata. - /// - /// The metadata. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract void SetMetadata (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sets the specified metadata. - /// - /// - /// Asynchronously sets the specified metadata. - /// - /// An asynchronous task context. - /// The metadata. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder does not support metadata. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public abstract Task SetMetadataAsync (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Occurs when a remote message store receives an alert message from the server. - /// - /// - /// The event is raised whenever the mail server sends an - /// alert message. - /// - public event EventHandler Alert; - - /// - /// Raise the alert event. - /// - /// - /// Raises the alert event. - /// - /// The alert message. - /// - /// is null. - /// - protected virtual void OnAlert (string message) - { - var handler = Alert; - - if (handler != null) - handler (this, new AlertEventArgs (message)); - } - - /// - /// Occurs when a folder is created. - /// - /// - /// The event is emitted when a new folder is created. - /// - public event EventHandler FolderCreated; - - /// - /// Raise the folder created event. - /// - /// - /// Raises the folder created event. - /// - /// The folder that was just created. - protected virtual void OnFolderCreated (IMailFolder folder) - { - var handler = FolderCreated; - - if (handler != null) - handler (this, new FolderCreatedEventArgs (folder)); - } - - /// - /// Occurs when metadata changes. - /// - /// - /// The event is emitted when metadata changes. - /// - public event EventHandler MetadataChanged; - - /// - /// Raise the metadata changed event. - /// - /// - /// Raises the metadata changed event. - /// - /// The metadata that changed. - protected virtual void OnMetadataChanged (Metadata metadata) - { - var handler = MetadataChanged; - - if (handler != null) - handler (this, new MetadataChangedEventArgs (metadata)); - } - } -} diff --git a/src/MailKit/MailTransport.cs b/src/MailKit/MailTransport.cs deleted file mode 100644 index 418d2b9..0000000 --- a/src/MailKit/MailTransport.cs +++ /dev/null @@ -1,507 +0,0 @@ -// -// MailTransport.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -namespace MailKit { - /// - /// An abstract mail transport implementation. - /// - /// - /// An abstract mail transport implementation. - /// - public abstract class MailTransport : MailService, IMailTransport - { - static readonly FormatOptions DefaultOptions; - - static MailTransport () - { - var options = FormatOptions.Default.Clone (); - options.HiddenHeaders.Add (HeaderId.ContentLength); - options.HiddenHeaders.Add (HeaderId.ResentBcc); - options.HiddenHeaders.Add (HeaderId.Bcc); - options.NewLineFormat = NewLineFormat.Dos; - - DefaultOptions = options; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The protocol logger. - /// - /// is null. - /// - protected MailTransport (IProtocolLogger protocolLogger) : base (protocolLogger) - { - } - - /// - /// Send the specified message. - /// - /// - /// Sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// - /// - /// - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The send command failed. - /// - /// - /// A protocol exception occurred. - /// - public virtual void Send (MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - Send (DefaultOptions, message, cancellationToken, progress); - } - - /// - /// Asynchronously send the specified message. - /// - /// - /// Asynchronously sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// An asynchronous task context. - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The send command failed. - /// - /// - /// A protocol exception occurred. - /// - public virtual Task SendAsync (MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return SendAsync (DefaultOptions, message, cancellationToken, progress); - } - - /// - /// Send the specified message using the supplied sender and recipients. - /// - /// - /// Sends the specified message using the supplied sender and recipients. - /// - /// The message. - /// The mailbox address to use for sending the message. - /// The mailbox addresses that should receive the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The send command failed. - /// - /// - /// A protocol exception occurred. - /// - public virtual void Send (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - Send (DefaultOptions, message, sender, recipients, cancellationToken, progress); - } - - /// - /// Asynchronously send the specified message using the supplied sender and recipients. - /// - /// - /// Asynchronously sends the specified message using the supplied sender and recipients. - /// - /// An asynchronous task context. - /// The message. - /// The mailbox address to use for sending the message. - /// The mailbox addresses that should receive the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The send command failed. - /// - /// - /// A protocol exception occurred. - /// - public virtual Task SendAsync (MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return SendAsync (DefaultOptions, message, sender, recipients, cancellationToken, progress); - } - - /// - /// Send the specified message. - /// - /// - /// Sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// - /// - /// - /// The formatting options. - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation has been canceled. - /// - /// - /// Internationalized formatting was requested but is not supported by the transport. - /// - /// - /// An I/O error occurred. - /// - /// - /// The send command failed. - /// - /// - /// A protocol exception occurred. - /// - public abstract void Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously send the specified message. - /// - /// - /// Asynchronously sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// An asynchronous task context. - /// The formatting options. - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation has been canceled. - /// - /// - /// Internationalized formatting was requested but is not supported by the transport. - /// - /// - /// An I/O error occurred. - /// - /// - /// The send command failed. - /// - /// - /// A protocol exception occurred. - /// - public abstract Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Send the specified message using the supplied sender and recipients. - /// - /// - /// Sends the specified message using the supplied sender and recipients. - /// - /// The formatting options. - /// The message. - /// The mailbox address to use for sending the message. - /// The mailbox addresses that should receive the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation has been canceled. - /// - /// - /// Internationalized formatting was requested but is not supported by the transport. - /// - /// - /// An I/O error occurred. - /// - /// - /// The send command failed. - /// - /// - /// A protocol exception occurred. - /// - public abstract void Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously send the specified message using the supplied sender and recipients. - /// - /// - /// Asynchronously sends the specified message using the supplied sender and recipients. - /// - /// An asynchronous task context. - /// The formatting options. - /// The message. - /// The mailbox address to use for sending the message. - /// The mailbox addresses that should receive the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation has been canceled. - /// - /// - /// Internationalized formatting was requested but is not supported by the transport. - /// - /// - /// An I/O error occurred. - /// - /// - /// The send command failed. - /// - /// - /// A protocol exception occurred. - /// - public abstract Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Occurs when a message is successfully sent via the transport. - /// - /// - /// The event will be emitted each time a message is successfully sent. - /// - public event EventHandler MessageSent; - - /// - /// Raise the message sent event. - /// - /// - /// Raises the message sent event. - /// - /// The message sent event args. - protected virtual void OnMessageSent (MessageSentEventArgs e) - { - var handler = MessageSent; - - if (handler != null) - handler (this, e); - } - } -} diff --git a/src/MailKit/MessageEventArgs.cs b/src/MailKit/MessageEventArgs.cs deleted file mode 100644 index 6379c10..0000000 --- a/src/MailKit/MessageEventArgs.cs +++ /dev/null @@ -1,98 +0,0 @@ -// -// MessageEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Event args used when the state of a message changes. - /// - /// - /// Event args used when the state of a message changes. - /// - public class MessageEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// - /// is out of range. - /// - public MessageEventArgs (int index) - { - if (index < 0) - throw new ArgumentOutOfRangeException (nameof (index)); - - Index = index; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The unique id of the message. - /// - /// is out of range. - /// - public MessageEventArgs (int index, UniqueId uid) - { - if (index < 0) - throw new ArgumentOutOfRangeException (nameof (index)); - - Index = index; - UniqueId = uid; - } - - /// - /// Gets the index of the message that changed. - /// - /// - /// Gets the index of the message that changed. - /// - /// The index of the message. - public int Index { - get; private set; - } - - /// - /// Gets the unique ID of the message that changed, if available. - /// - /// - /// Gets the unique ID of the message that changed, if available. - /// - /// The unique ID of the message. - public UniqueId? UniqueId { - get; internal set; - } - } -} diff --git a/src/MailKit/MessageFlags.cs b/src/MailKit/MessageFlags.cs deleted file mode 100644 index 3c80c0b..0000000 --- a/src/MailKit/MessageFlags.cs +++ /dev/null @@ -1,78 +0,0 @@ -// -// MessageFlags.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// An enumeration of message flags. - /// - /// - /// An enumeration of message flags. - /// - [Flags] - public enum MessageFlags { - /// - /// No message flags are set. - /// - None = 0, - - /// - /// The message has been read. - /// - Seen = 1 << 0, - - /// - /// The message has been answered (replied to). - /// - Answered = 1 << 1, - - /// - /// The message has been flagged for importance. - /// - Flagged = 1 << 2, - - /// - /// The message has been marked for deletion. - /// - Deleted = 1 << 3, - - /// - /// The message is marked as a draft. - /// - Draft = 1 << 4, - - /// - /// The message has just recently arrived in the folder. - /// - Recent = 1 << 5, - - /// - /// User-defined flags are allowed by the folder. - /// - UserDefined = 1 << 6, - } -} diff --git a/src/MailKit/MessageFlagsChangedEventArgs.cs b/src/MailKit/MessageFlagsChangedEventArgs.cs deleted file mode 100644 index 868cb7a..0000000 --- a/src/MailKit/MessageFlagsChangedEventArgs.cs +++ /dev/null @@ -1,269 +0,0 @@ -// -// MessageFlagsChangedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -namespace MailKit { - /// - /// Event args for the event. - /// - /// - /// Event args for the event. - /// - public class MessageFlagsChangedEventArgs : MessageEventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - internal MessageFlagsChangedEventArgs (int index) : base (index) - { - Keywords = new HashSet (StringComparer.OrdinalIgnoreCase); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The message flags. - public MessageFlagsChangedEventArgs (int index, MessageFlags flags) : base (index) - { - Keywords = new HashSet (StringComparer.OrdinalIgnoreCase); - Flags = flags; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The message flags. - /// The user-defined message flags. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MessageFlagsChangedEventArgs (int index, MessageFlags flags, HashSet keywords) : base (index) - { - if (keywords == null) - throw new ArgumentNullException (nameof (keywords)); - - Keywords = keywords; - Flags = flags; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The message flags. - /// The modification sequence value. - /// - /// is out of range. - /// - public MessageFlagsChangedEventArgs (int index, MessageFlags flags, ulong modseq) : base (index) - { - Keywords = new HashSet (StringComparer.OrdinalIgnoreCase); - ModSeq = modseq; - Flags = flags; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The message flags. - /// The user-defined message flags. - /// The modification sequence value. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MessageFlagsChangedEventArgs (int index, MessageFlags flags, HashSet keywords, ulong modseq) : base (index) - { - if (keywords == null) - throw new ArgumentNullException (nameof (keywords)); - - Keywords = keywords; - ModSeq = modseq; - Flags = flags; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The unique id of the message. - /// The message flags. - /// - /// is out of range. - /// - public MessageFlagsChangedEventArgs (int index, UniqueId uid, MessageFlags flags) : base (index, uid) - { - Keywords = new HashSet (StringComparer.OrdinalIgnoreCase); - Flags = flags; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The unique id of the message. - /// The message flags. - /// The user-defined message flags. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MessageFlagsChangedEventArgs (int index, UniqueId uid, MessageFlags flags, HashSet keywords) : base (index, uid) - { - if (keywords == null) - throw new ArgumentNullException (nameof (keywords)); - - Keywords = keywords; - Flags = flags; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The unique id of the message. - /// The message flags. - /// The modification sequence value. - /// - /// is out of range. - /// - public MessageFlagsChangedEventArgs (int index, UniqueId uid, MessageFlags flags, ulong modseq) : base (index, uid) - { - Keywords = new HashSet (StringComparer.OrdinalIgnoreCase); - ModSeq = modseq; - Flags = flags; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The unique id of the message. - /// The message flags. - /// The user-defined message flags. - /// The modification sequence value. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MessageFlagsChangedEventArgs (int index, UniqueId uid, MessageFlags flags, HashSet keywords, ulong modseq) : base (index, uid) - { - if (keywords == null) - throw new ArgumentNullException (nameof (keywords)); - - Keywords = keywords; - ModSeq = modseq; - Flags = flags; - } - - /// - /// Gets the updated message flags. - /// - /// - /// Gets the updated message flags. - /// - /// The updated message flags. - public MessageFlags Flags { - get; internal set; - } - - /// - /// Gets the updated user-defined message flags. - /// - /// - /// Gets the updated user-defined message flags. - /// - /// The updated user-defined message flags. - public HashSet Keywords { - get; private set; - } - - /// - /// Gets the updated user-defined message flags. - /// - /// - /// Gets the updated user-defined message flags. - /// - /// The updated user-defined message flags. - [Obsolete ("Use Keywords instead.")] - public HashSet UserFlags { - get { return Keywords; } - } - - /// - /// Gets the updated mod-sequence value of the message, if available. - /// - /// - /// Gets the updated mod-sequence value of the message, if available. - /// - /// The mod-sequence value. - public ulong? ModSeq { - get; internal set; - } - } -} diff --git a/src/MailKit/MessageLabelsChangedEventArgs.cs b/src/MailKit/MessageLabelsChangedEventArgs.cs deleted file mode 100644 index 2498af3..0000000 --- a/src/MailKit/MessageLabelsChangedEventArgs.cs +++ /dev/null @@ -1,170 +0,0 @@ -// -// LabelsChangedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace MailKit { - /// - /// Event args for the event. - /// - /// - /// Event args for the event. - /// - 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. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The message labels. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MessageLabelsChangedEventArgs (int index, IList labels) : base (index) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - Labels = new ReadOnlyCollection (labels); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The message labels. - /// The modification sequence value. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MessageLabelsChangedEventArgs (int index, IList labels, ulong modseq) : base (index) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - Labels = new ReadOnlyCollection (labels); - ModSeq = modseq; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The unique id of the message. - /// The message labels. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MessageLabelsChangedEventArgs (int index, UniqueId uid, IList labels) : base (index, uid) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - Labels = new ReadOnlyCollection (labels); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The unique id of the message. - /// The message labels. - /// The modification sequence value. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MessageLabelsChangedEventArgs (int index, UniqueId uid, IList labels, ulong modseq) : base (index, uid) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - Labels = new ReadOnlyCollection (labels); - ModSeq = modseq; - } - - /// - /// Gets the updated labels. - /// - /// - /// Gets the updated labels. - /// - /// The updated labels. - public IList Labels { - get; internal set; - } - - /// - /// Gets the updated mod-sequence value of the message, if available. - /// - /// - /// Gets the updated mod-sequence value of the message, if available. - /// - /// The mod-sequence value. - public ulong? ModSeq { - get; internal set; - } - } -} diff --git a/src/MailKit/MessageNotFoundException.cs b/src/MailKit/MessageNotFoundException.cs deleted file mode 100644 index d7ca582..0000000 --- a/src/MailKit/MessageNotFoundException.cs +++ /dev/null @@ -1,92 +0,0 @@ -// -// MessageNotFoundException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit { - /// - /// The exception that is thrown when a message (or body part) could not be found. - /// - /// - /// This exception is thrown by methods such as - /// IMailFolder.GetMessage, - /// IMailFolder.GetBodyPart, or - /// IMailFolder.GetStream - /// when the server's response does not contain the message, body part, or stream data requested. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class MessageNotFoundException : Exception - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Deserializes a . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected MessageNotFoundException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The inner exception. - /// - /// - public MessageNotFoundException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public MessageNotFoundException (string message) : base (message) - { - } - } -} diff --git a/src/MailKit/MessageSentEventArgs.cs b/src/MailKit/MessageSentEventArgs.cs deleted file mode 100644 index effeed8..0000000 --- a/src/MailKit/MessageSentEventArgs.cs +++ /dev/null @@ -1,87 +0,0 @@ -// -// MessageSentEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit; - -namespace MailKit { - /// - /// Event args used when a message is successfully sent. - /// - /// - /// Event args used when message is successfully sent. - /// - public class MessageSentEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message that was just sent. - /// The response from the server. - /// - /// is null. - /// -or- - /// is null. - /// - public MessageSentEventArgs (MimeMessage message, string response) - { - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (response == null) - throw new ArgumentNullException (nameof (response)); - - Message = message; - Response = response; - } - - /// - /// Get the message that was just sent. - /// - /// - /// Gets the message that was just sent. - /// - /// The message. - public MimeMessage Message { - get; private set; - } - - /// - /// Get the server's response. - /// - /// - /// Gets the server's response. - /// - /// The response. - public string Response { - get; private set; - } - } -} diff --git a/src/MailKit/MessageSorter.cs b/src/MailKit/MessageSorter.cs deleted file mode 100644 index f967bef..0000000 --- a/src/MailKit/MessageSorter.cs +++ /dev/null @@ -1,295 +0,0 @@ -// -// MessageSorter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Collections.Generic; - -using MimeKit; -using MailKit.Search; - -namespace MailKit { - /// - /// Routines for sorting messages. - /// - /// - /// Routines for sorting messages. - /// - public static class MessageSorter - { - class MessageComparer : IComparer where T : IMessageSummary - { - readonly IList orderBy; - - public MessageComparer (IList orderBy) - { - this.orderBy = orderBy; - } - - #region IComparer implementation - - static int CompareDisplayNames (InternetAddressList list1, InternetAddressList list2) - { - var m1 = list1.Mailboxes.GetEnumerator (); - var m2 = list2.Mailboxes.GetEnumerator (); - bool n1 = m1.MoveNext (); - bool n2 = m2.MoveNext (); - - while (n1 && n2) { - var name1 = m1.Current.Name ?? string.Empty; - var name2 = m2.Current.Name ?? string.Empty; - int cmp; - - if ((cmp = string.Compare (name1, name2, StringComparison.OrdinalIgnoreCase)) != 0) - return cmp; - - n1 = m1.MoveNext (); - n2 = m2.MoveNext (); - } - - return n1 ? 1 : (n2 ? -1 : 0); - } - - static int CompareMailboxAddresses (InternetAddressList list1, InternetAddressList list2) - { - var m1 = list1.Mailboxes.GetEnumerator (); - var m2 = list2.Mailboxes.GetEnumerator (); - bool n1 = m1.MoveNext (); - bool n2 = m2.MoveNext (); - - while (n1 && n2) { - int cmp; - - if ((cmp = string.Compare (m1.Current.Address, m2.Current.Address, StringComparison.OrdinalIgnoreCase)) != 0) - return cmp; - - n1 = m1.MoveNext (); - n2 = m2.MoveNext (); - } - - return n1 ? 1 : (n2 ? -1 : 0); - } - - public int Compare (T x, T y) - { - int cmp = 0; - - for (int i = 0; i < orderBy.Count; i++) { - switch (orderBy[i].Type) { - 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 xvalue = xannotation?.Properties[annotation.Attribute] ?? string.Empty; - var yvalue = yannotation?.Properties[annotation.Attribute] ?? string.Empty; - - cmp = string.Compare (xvalue, yvalue, StringComparison.OrdinalIgnoreCase); - break; - case OrderByType.Arrival: - cmp = x.Index.CompareTo (y.Index); - break; - case OrderByType.Cc: - cmp = CompareMailboxAddresses (x.Envelope.Cc, y.Envelope.Cc); - break; - case OrderByType.Date: - cmp = x.Date.CompareTo (y.Date); - break; - case OrderByType.DisplayFrom: - cmp = CompareDisplayNames (x.Envelope.From, y.Envelope.From); - break; - case OrderByType.From: - cmp = CompareMailboxAddresses (x.Envelope.From, y.Envelope.From); - break; - case OrderByType.ModSeq: - 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; - - cmp = xsize.CompareTo (ysize); - break; - case OrderByType.Subject: - 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); - break; - case OrderByType.To: - cmp = CompareMailboxAddresses (x.Envelope.To, y.Envelope.To); - break; - } - - if (cmp == 0) - continue; - - return orderBy[i].Order == SortOrder.Descending ? cmp * -1 : cmp; - } - - return cmp; - } - - #endregion - } - - static MessageSummaryItems GetMessageSummaryItems (IList orderBy) - { - var items = MessageSummaryItems.None; - - for (int i = 0; i < orderBy.Count; i++) { - switch (orderBy[i].Type) { - case OrderByType.Annotation: - items |= MessageSummaryItems.Annotations; - break; - case OrderByType.Arrival: - break; - case OrderByType.Cc: - case OrderByType.Date: - case OrderByType.DisplayFrom: - case OrderByType.DisplayTo: - case OrderByType.From: - case OrderByType.Subject: - case OrderByType.To: - items |= MessageSummaryItems.Envelope; - break; - case OrderByType.ModSeq: - items |= MessageSummaryItems.ModSeq; - break; - case OrderByType.Size: - items |= MessageSummaryItems.Size; - break; - } - } - - return items; - } - - /// - /// Sorts the messages by the specified ordering. - /// - /// - /// Sorts the messages by the specified ordering. - /// - /// The sorted messages. - /// The message items must implement the interface. - /// The messages to sort. - /// The sort ordering. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// contains one or more items that is missing information needed for sorting. - /// -or- - /// is an empty list. - /// - public static IList Sort (this IEnumerable messages, IList orderBy) where T : IMessageSummary - { - if (messages == null) - throw new ArgumentNullException (nameof (messages)); - - if (orderBy == null) - throw new ArgumentNullException (nameof (orderBy)); - - if (orderBy.Count == 0) - throw new ArgumentException ("No sort order provided.", nameof (orderBy)); - - var requiredFields = GetMessageSummaryItems (orderBy); - var list = new List (); - - foreach (var message in messages) { - if ((message.Fields & requiredFields) != requiredFields) - throw new ArgumentException ("One or more messages is missing information needed for sorting.", nameof (messages)); - - list.Add (message); - } - - if (list.Count < 2) - return list; - - var comparer = new MessageComparer (orderBy); - - list.Sort (comparer); - - return list; - } - - /// - /// Sorts the messages by the specified ordering. - /// - /// - /// Sorts the messages by the specified ordering. - /// - /// The sorted messages. - /// The message items must implement the interface. - /// The messages to sort. - /// The sort ordering. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// contains one or more items that is missing information needed for sorting. - /// -or- - /// is an empty list. - /// - public static void Sort (this List messages, IList orderBy) where T : IMessageSummary - { - if (messages == null) - throw new ArgumentNullException (nameof (messages)); - - if (orderBy == null) - throw new ArgumentNullException (nameof (orderBy)); - - if (orderBy.Count == 0) - throw new ArgumentException ("No sort order provided.", nameof (orderBy)); - - var requiredFields = GetMessageSummaryItems (orderBy); - - for (int i = 0; i < messages.Count; i++) { - if ((messages[i].Fields & requiredFields) != requiredFields) - throw new ArgumentException ("One or more messages is missing information needed for sorting.", nameof (messages)); - } - - if (messages.Count < 2) - return; - - var comparer = new MessageComparer (orderBy); - - messages.Sort (comparer); - } - } -} diff --git a/src/MailKit/MessageSummary.cs b/src/MailKit/MessageSummary.cs deleted file mode 100644 index 798a24d..0000000 --- a/src/MailKit/MessageSummary.cs +++ /dev/null @@ -1,758 +0,0 @@ -// -// MessageSummary.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Collections.Generic; - -using MimeKit; -using MimeKit.Utils; - -namespace MailKit { - /// - /// A summary of a message. - /// - /// - /// The Fetch and - /// FetchAsync methods - /// return lists of items. - /// The properties of the that will be available - /// depend on the passed to the aformentioned method. - /// - public class MessageSummary : IMessageSummary - { - int threadableReplyDepth = -1; - string normalizedSubject; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// - /// is negative. - /// - public MessageSummary (int index) - { - if (index < 0) - throw new ArgumentOutOfRangeException (nameof (index)); - - Keywords = new HashSet (StringComparer.OrdinalIgnoreCase); - Index = index; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The folder that the message belongs to. - /// The message index. - /// - /// is null. - /// - /// - /// is negative. - /// - public MessageSummary (IMailFolder folder, int index) : this (index) - { - if (folder == null) - throw new ArgumentNullException (nameof (folder)); - - Folder = folder; - } - - void UpdateThreadableSubject () - { - if (normalizedSubject != null) - return; - - if (Envelope?.Subject != null) { - normalizedSubject = MessageThreader.GetThreadableSubject (Envelope.Subject, out threadableReplyDepth); - } else { - normalizedSubject = string.Empty; - threadableReplyDepth = 0; - } - } - - /// - /// Get the folder that the message belongs to. - /// - /// - /// Gets the folder that the message belongs to, if available. - /// - /// The folder. - public IMailFolder Folder { - get; private set; - } - - /// - /// Get a bitmask of fields that have been populated. - /// - /// - /// Gets a bitmask of fields that have been populated. - /// - /// The fields that have been populated. - public MessageSummaryItems Fields { - get; internal set; - } - - /// - /// Gets the body structure of the message, if available. - /// - /// - /// The body will be one of , - /// , , - /// or . - /// This property will only be set if either the - /// flag or the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The body structure of the message. - public BodyPart Body { - get; set; - } - - static BodyPart GetMultipartRelatedRoot (BodyPartMultipart related) - { - string start = related.ContentType.Parameters["start"]; - string contentId; - - if (start == null) - return related.BodyParts.Count > 0 ? related.BodyParts[0] : null; - - if ((contentId = MimeUtils.EnumerateReferences (start).FirstOrDefault ()) == null) - contentId = start; - - var cid = new Uri (string.Format ("cid:{0}", contentId)); - - for (int i = 0; i < related.BodyParts.Count; i++) { - var basic = related.BodyParts[i] as BodyPartBasic; - - if (basic != null && (basic.ContentId == contentId || basic.ContentLocation == cid)) - return basic; - - var multipart = related.BodyParts[i] as BodyPartMultipart; - - if (multipart != null && multipart.ContentLocation == cid) - return multipart; - } - - return null; - } - - static bool TryGetMultipartAlternativeBody (BodyPartMultipart multipart, bool html, 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--) { - var multi = multipart.BodyParts[i] as BodyPartMultipart; - BodyPartText text = null; - - if (multi != null) { - if (multi.ContentType.IsMimeType ("multipart", "related")) { - text = GetMultipartRelatedRoot (multi) as BodyPartText; - } else if (multi.ContentType.IsMimeType ("multipart", "alternative")) { - // Note: nested multipart/alternatives make no sense... yet here we are. - if (TryGetMultipartAlternativeBody (multi, html, out body)) - return true; - } - } else { - text = multipart.BodyParts[i] as BodyPartText; - } - - if (text != null && (html ? text.IsHtml : text.IsPlain)) { - body = text; - return true; - } - } - - body = null; - - return false; - } - - static bool TryGetMessageBody (BodyPartMultipart multipart, bool html, out BodyPartText body) - { - BodyPartMultipart multi; - BodyPartText text; - - if (multipart.ContentType.IsMimeType ("multipart", "alternative")) - return TryGetMultipartAlternativeBody (multipart, html, out body); - - if (!multipart.ContentType.IsMimeType ("multipart", "related")) { - // Note: This is probably a multipart/mixed... and if not, we can still treat it like it is. - for (int i = 0; i < multipart.BodyParts.Count; i++) { - multi = multipart.BodyParts[i] as BodyPartMultipart; - - // descend into nested multiparts, if there are any... - if (multi != null) { - if (TryGetMessageBody (multi, html, out body)) - return true; - - // The text body should never come after a multipart. - break; - } - - text = multipart.BodyParts[i] as BodyPartText; - - // Look for the first non-attachment text part (realistically, the body text will - // preceed any attachments, but I'm not sure we can rely on that assumption). - if (text != null && !text.IsAttachment) { - if (html ? text.IsHtml : text.IsPlain) { - body = text; - return true; - } - - // Note: the first text/* part in a multipart/mixed is the text body. - // If it's not in the format we're looking for, then it doesn't exist. - break; - } - } - } else { - // Note: If the multipart/related root document is HTML, then this is the droid we are looking for. - var root = GetMultipartRelatedRoot (multipart); - - text = root as BodyPartText; - - if (text != null) { - body = (html ? text.IsHtml : text.IsPlain) ? text : null; - return body != null; - } - - // maybe the root is another multipart (like multipart/alternative)? - multi = root as BodyPartMultipart; - - if (multi != null) - return TryGetMessageBody (multi, html, out body); - } - - body = null; - - return false; - } - - /// - /// Gets the text body part of the message if it exists. - /// - /// - /// Gets the text/plain body part of the message. - /// This property will only be usable if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// - /// - /// - /// The text body if it exists; otherwise, null. - public BodyPartText TextBody { - get { - var multipart = Body as BodyPartMultipart; - - if (multipart != null) { - BodyPartText plain; - - if (TryGetMessageBody (multipart, false, out plain)) - return plain; - } else { - var text = Body as BodyPartText; - - if (text != null && text.IsPlain) - return text; - } - - return null; - } - } - - /// - /// Gets the html body part of the message if it exists. - /// - /// - /// Gets the text/html body part of the message. - /// This property will only be usable if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The html body if it exists; otherwise, null. - public BodyPartText HtmlBody { - get { - var multipart = Body as BodyPartMultipart; - - if (multipart != null) { - BodyPartText html; - - if (TryGetMessageBody (multipart, true, out html)) - return html; - } else { - var text = Body as BodyPartText; - - if (text != null && text.IsHtml) - return text; - } - - return null; - } - } - - static IEnumerable EnumerateBodyParts (BodyPart entity, bool attachmentsOnly) - { - if (entity == null) - yield break; - - var multipart = entity as BodyPartMultipart; - - if (multipart != null) { - foreach (var subpart in multipart.BodyParts) { - foreach (var part in EnumerateBodyParts (subpart, attachmentsOnly)) - yield return part; - } - - yield break; - } - - var basic = (BodyPartBasic) entity; - - if (attachmentsOnly && !basic.IsAttachment) - yield break; - - yield return basic; - } - - /// - /// Gets the body parts of the message. - /// - /// - /// Traverses over the , enumerating all of the - /// objects. - /// This property will only be usable if either the - /// flag or the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The body parts. - public IEnumerable BodyParts { - get { return EnumerateBodyParts (Body, false); } - } - - /// - /// Gets the attachments. - /// - /// - /// Traverses over the , enumerating all of the - /// objects that have a Content-Disposition - /// header set to "attachment". - /// This property will only be usable if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// - /// - /// - /// The attachments. - public IEnumerable Attachments { - get { return EnumerateBodyParts (Body, true); } - } - - /// - /// Gets the preview text of the message. - /// - /// - /// The preview text is a short snippet of the beginning of the message - /// text, typically shown in a mail client's message list to provide the user - /// with a sense of what the message is about. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The preview text. - public string PreviewText { - get; set; - } - - /// - /// Gets the envelope of the message, if available. - /// - /// - /// The envelope of a message contains information such as the - /// date the message was sent, the subject of the message, - /// the sender of the message, who the message was sent to, - /// which message(s) the message may be in reply to, - /// and the message id. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The envelope of the message. - public Envelope Envelope { - get; set; - } - - /// - /// Gets the normalized subject. - /// - /// - /// A normalized Subject header value where prefixes such as - /// "Re:", "Re[#]:", etc have been pruned. - /// - /// The normalized subject. - public string NormalizedSubject { - get { - UpdateThreadableSubject (); - - return normalizedSubject; - } - } - - /// - /// Gets whether or not the message is a reply. - /// - /// - /// This value should be based on whether the message subject contained any "Re:" or "Fwd:" prefixes. - /// - /// true if the message is a reply; otherwise, false. - public bool IsReply { - get { - UpdateThreadableSubject (); - - return threadableReplyDepth != 0; - } - } - - /// - /// Gets the Date header value. - /// - /// - /// Gets the Date header value. If the Date header is not present, the arrival date is used. - /// If neither are known, is returned. - /// - /// The date. - public DateTimeOffset Date { - get { return Envelope?.Date ?? InternalDate ?? DateTimeOffset.MinValue; } - } - - /// - /// Gets the message flags, if available. - /// - /// - /// Gets the message flags, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The message flags. - public MessageFlags? Flags { - get; set; - } - - /// - /// Gets the user-defined message flags, if available. - /// - /// - /// Gets the user-defined message flags, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The user-defined message flags. - public HashSet Keywords { - get; set; - } - - /// - /// Gets the user-defined message flags, if available. - /// - /// - /// Gets the user-defined message flags, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The user-defined message flags. - [Obsolete ("Use Keywords instead.")] - public HashSet UserFlags { - get { return Keywords; } - set { Keywords = value; } - } - - /// - /// Gets the message annotations, if available. - /// - /// - /// Gets the message annotations, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The message annotations. - public IList Annotations { - get; set; - } - - /// - /// Gets the list of headers, if available. - /// - /// - /// Gets the list of headers, if available. - /// This property will only be set if - /// is specified in a call to one of the - /// Fetch - /// or FetchAsync - /// methods or specific headers are requested via a one of the Fetch or FetchAsync methods - /// that accept list of specific headers to request for each message such as - /// . - /// - /// - /// The list of headers. - public HeaderList Headers { - get; set; - } - - /// - /// Gets the internal date of the message (i.e. the "received" date), if available. - /// - /// - /// Gets the internal date of the message (i.e. the "received" date), if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The internal date of the message. - public DateTimeOffset? InternalDate { - get; set; - } - - /// - /// Gets the size of the message, in bytes, if available. - /// - /// - /// Gets the size of the message, in bytes, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The size of the message. - public uint? Size { - get; set; - } - - /// - /// Gets the mod-sequence value for the message, if available. - /// - /// - /// Gets the mod-sequence value for the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The mod-sequence value. - public ulong? ModSeq { - get; set; - } - - /// - /// Gets the message-ids that the message references, if available. - /// - /// - /// Gets the message-ids that the message references, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The references. - public MessageIdList References { - get; set; - } - - /// - /// Get the globally unique identifier for the message, if available. - /// - /// - /// Gets the globally unique identifier of the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// This property maps to the EMAILID value defined in the - /// OBJECTID extension. - /// - /// The globally unique message identifier. - public string EmailId { - get; set; - } - - /// - /// Get the globally unique identifier for the message, if available. - /// - /// - /// Gets the globally unique identifier of the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// This property maps to the EMAILID value defined in the - /// OBJECTID extension. - /// - /// The globally unique message identifier. - [Obsolete ("Use EmailId instead.")] - public string Id { - get { return EmailId; } - } - - /// - /// Get the globally unique thread identifier for the message, if available. - /// - /// - /// Gets the globally unique thread identifier for the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// This property maps to the THREADID value defined in the - /// OBJECTID extension. - /// - /// The globally unique thread identifier. - public string ThreadId { - get; set; - } - - /// - /// Gets the unique identifier of the message, if available. - /// - /// - /// Gets the unique identifier of the message, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The uid of the message. - public UniqueId UniqueId { - get; set; - } - - /// - /// Gets the index of the message. - /// - /// - /// Gets the index of the message. - /// This property is always set. - /// - /// The index of the message. - public int Index { - get; internal set; - } - - #region GMail extension properties - - /// - /// Gets the GMail message identifier, if available. - /// - /// - /// Gets the GMail message identifier, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The GMail message identifier. - public ulong? GMailMessageId { - get; set; - } - - /// - /// Gets the GMail thread identifier, if available. - /// - /// - /// Gets the GMail thread identifier, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The GMail thread identifier. - public ulong? GMailThreadId { - get; set; - } - - /// - /// Gets the list of GMail labels, if available. - /// - /// - /// Gets the list of GMail labels, if available. - /// This property will only be set if the - /// flag is passed to - /// one of the Fetch - /// or FetchAsync - /// methods. - /// - /// The GMail labels. - public IList GMailLabels { - get; set; - } - - #endregion - } -} diff --git a/src/MailKit/MessageSummaryFetchedEventArgs.cs b/src/MailKit/MessageSummaryFetchedEventArgs.cs deleted file mode 100644 index 4598cac..0000000 --- a/src/MailKit/MessageSummaryFetchedEventArgs.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// MessageSummaryFetchedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Event args used when a message summary has been fetched from a folder. - /// - /// - /// Event args used when a message summary has been fetched from a folder. - /// - public class MessageSummaryFetchedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new - /// - /// The message summary. - /// - /// is null. - /// - public MessageSummaryFetchedEventArgs (IMessageSummary message) - { - if (message == null) - throw new ArgumentNullException (nameof (message)); - - Message = message; - } - - /// - /// Get the message summary. - /// - /// - /// Gets the message summary. - /// - /// The message summary. - public IMessageSummary Message { - get; private set; - } - } -} diff --git a/src/MailKit/MessageSummaryItems.cs b/src/MailKit/MessageSummaryItems.cs deleted file mode 100644 index e331735..0000000 --- a/src/MailKit/MessageSummaryItems.cs +++ /dev/null @@ -1,222 +0,0 @@ -// -// FetchFlags.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// A bitfield of fields. - /// - /// - /// are used to specify which properties - /// of should be populated by calls to - /// , - /// , or - /// . - /// - [Flags] - public enum MessageSummaryItems { - /// - /// Don't fetch any summary items. - /// - None = 0, - - /// - /// Fetch the . - /// Fetches all ANNOATION values as defined in - /// rfc5257. - /// - Annotations = 1 << 0, - - /// - /// Fetch the . - /// Fetches the BODY value as defined in - /// rfc3501. - /// Unlike , Body will not populate the - /// parameters nor will it populate the - /// , - /// or properties of each - /// body part. This makes Body far less useful than BodyStructure especially when - /// it is desirable to determine whether or not a body part is an attachment. - /// - Body = 1 << 1, - - /// - /// Fetch the (but with more details than ). - /// Fetches the BODYSTRUCTURE value as defined in - /// rfc3501. - /// Unlike , BodyStructure will also populate the - /// parameters as well as the - /// , - /// and properties of each - /// body part. The Content-Disposition information is especially important when trying to - /// determine whether or not a body part is an attachment, for example. - /// - BodyStructure = 1 << 2, - - /// - /// Fetch the . - /// Fetches the ENVELOPE value as defined in - /// rfc3501. - /// - Envelope = 1 << 3, - - /// - /// Fetch the . - /// Fetches the FLAGS value as defined in - /// rfc3501. - /// - Flags = 1 << 4, - - /// - /// Fetch the . - /// Fetches the INTERNALDATE value as defined in - /// rfc3501. - /// - InternalDate = 1 << 5, - - /// - /// Fetch the . - /// Fetches the RFC822.SIZE value as defined in - /// rfc3501. - /// - Size = 1 << 6, - - /// - /// Fetch the . - /// Fetches the MODSEQ value as defined in - /// rfc4551. - /// - ModSeq = 1 << 7, - - /// - /// Fetch the . - /// - References = 1 << 8, - - /// - /// Fetch the . - /// Fetches the UID value as defined in - /// rfc3501. - /// - UniqueId = 1 << 9, - - /// - /// Fetch the . - /// Fetches the EMAILID value as defined in - /// rfc8474. - /// - EmailId = 1 << 10, - - /// - /// Fetch the . - /// Fetches the EMAILID value as defined in - /// rfc8474. - /// - [Obsolete ("Use EmailId instead.")] - Id = EmailId, - - /// - /// Fetch the . - /// Fetches the THREADID value as defined in - /// rfc8474. - /// - ThreadId = 1 << 11, - - #region GMail extension items - - /// - /// Fetch the . - /// Fetches the X-GM-MSGID value as defined in Google's - /// IMAP extensions - /// documentation. - /// - GMailMessageId = 1 << 12, - - /// - /// Fetch the . - /// Fetches the X-GM-THRID value as defined in Google's - /// IMAP extensions - /// documentation. - /// - GMailThreadId = 1 << 13, - - /// - /// Fetch the . - /// Fetches the X-GM-LABELS value as defined in Google's - /// IMAP extensions - /// documentation. - /// - GMailLabels = 1 << 14, - - #endregion - - /// - /// Fetch the the complete list of for each message. - /// - Headers = 1 << 15, - - /// - /// Fetch the . - /// This property is quite expensive to calculate because it is not an - /// item that is cached on the IMAP server. Instead, MailKit must download a hunk of the - /// message body so that it can decode and parse it in order to generate a meaningful - /// text snippet. This usually involves downloading the first 512 bytes for text/plain - /// message bodies and the first 16 kilobytes for text/html message bodies. If a - /// message contains both a text/plain body and a text/html body, then the - /// text/plain content is used in order to reduce network traffic. - /// - PreviewText = 1 << 16, - - #region Macros - - /// - /// A macro for fetching the , , - /// , and values. - /// This macro maps to the equivalent ALL macro as defined in - /// rfc3501. - /// - All = Envelope | Flags | InternalDate | Size, - - /// - /// A macro for fetching the , , and - /// values. - /// This macro maps to the equivalent FAST macro as defined in - /// rfc3501. - /// - Fast = Flags | InternalDate | Size, - - /// - /// A macro for fetching the , , - /// , , and values. - /// This macro maps to the equivalent FULL macro as defined in - /// rfc3501. - /// - Full = Body | Envelope | Flags| InternalDate | Size, - - #endregion - } -} diff --git a/src/MailKit/MessageThread.cs b/src/MailKit/MessageThread.cs deleted file mode 100644 index c406239..0000000 --- a/src/MailKit/MessageThread.cs +++ /dev/null @@ -1,108 +0,0 @@ -// -// MessageThread.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Collections.Generic; - -namespace MailKit { - /// - /// A message thread. - /// - /// - /// A message thread. - /// - public class MessageThread - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new message thread node. - /// - /// The unique identifier of the message. - public MessageThread (UniqueId? uid) - { - Children = new List (); - UniqueId = uid; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new message thread node. - /// - /// The message summary. - public MessageThread (IMessageSummary message) - { - Children = new List (); - if (message != null && message.UniqueId.IsValid) - UniqueId = message?.UniqueId; - Message = message; - } - - /// - /// Gets the message summary, if available. - /// - /// - /// Gets the message summary, if available. - /// This property will only ever be set if the - /// was created by the . s that are - /// created by any of the - /// Thread or - /// ThreadAsync - /// methods will always be null. - /// - /// The message summary. - public IMessageSummary Message { - get; private set; - } - - /// - /// Gets the unique identifier of the message. - /// - /// - /// The unique identifier may be null if the message is missing from the - /// or from the list of messages provided to the - /// . - /// - /// The unique identifier. - public UniqueId? UniqueId { - // FIXME: this shouldn't be a nullable since we can just use UniqueId.Invalid - get; private set; - } - - /// - /// Gets the children. - /// - /// - /// Each child represents a reply to the message referenced by . - /// - /// The children. - public IList Children { - get; private set; - } - } -} diff --git a/src/MailKit/MessageThreader.cs b/src/MailKit/MessageThreader.cs deleted file mode 100644 index 1f965d5..0000000 --- a/src/MailKit/MessageThreader.cs +++ /dev/null @@ -1,603 +0,0 @@ -// -// MessageThreader.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections.Generic; - -using MimeKit; -using MimeKit.Utils; - -using MailKit.Search; - -namespace MailKit { - /// - /// Threads messages according to the algorithms defined in rfc5256. - /// - /// - /// Threads messages according to the algorithms defined in rfc5256. - /// - public static class MessageThreader - { - internal class ThreadableNode : IMessageSummary - { - public readonly List Children = new List (); - public IMessageSummary Message; - public ThreadableNode Parent; - - public ThreadableNode (IMessageSummary message) - { - Message = message; - } - - public bool HasParent { - get { return Parent != null; } - } - - public bool HasChildren { - get { return Children.Count > 0; } - } - - public IMailFolder Folder => null; - - public MessageSummaryItems Fields { - get { return MessageSummaryItems.UniqueId | MessageSummaryItems.Envelope | MessageSummaryItems.ModSeq | MessageSummaryItems.Size; } - } - - public BodyPart Body => null; - - public BodyPartText TextBody => null; - - public BodyPartText HtmlBody => null; - - public IEnumerable BodyParts => null; - - public IEnumerable Attachments => null; - - public string PreviewText => null; - - public Envelope Envelope { - get { return Message != null ? Message.Envelope : Children[0].Envelope; } - } - - public string NormalizedSubject { - get { return Message != null ? Message.NormalizedSubject : Children[0].NormalizedSubject; } - } - - public DateTimeOffset Date { - get { return Message != null ? Message.Date : Children[0].Date; } - } - - public bool IsReply { - get { return Message != null && Message.IsReply; } - } - - public MessageFlags? Flags => null; - - public HashSet Keywords => null; - - [Obsolete] - public HashSet UserFlags => null; - - public IList Annotations { - get { return Message != null ? Message.Annotations : Children[0].Annotations; } - } - - public HeaderList Headers => null; - - public DateTimeOffset? InternalDate => null; - - public uint? Size { - get { return Message != null ? Message.Size : Children[0].Size; } - } - - public ulong? ModSeq { - get { return Message != null ? Message.ModSeq : Children[0].ModSeq; } - } - - public MessageIdList References { - get { return Message != null ? Message.References : Children[0].References; } - } - - public string EmailId => null; - - [Obsolete] - public string Id => null; - - public string ThreadId => null; - - public UniqueId UniqueId { - get { return Message != null ? Message.UniqueId : Children[0].UniqueId; } - } - - public int Index { - get { return Message != null ? Message.Index : Children[0].Index; } - } - - public ulong? GMailMessageId => null; - - public ulong? GMailThreadId => null; - - public IList GMailLabels => null; - } - - static IDictionary CreateIdTable (IEnumerable messages) - { - var ids = new Dictionary (StringComparer.OrdinalIgnoreCase); - ThreadableNode node; - - foreach (var message in messages) { - if (message.Envelope == null) - throw new ArgumentException ("One or more messages is missing information needed for threading.", nameof (messages)); - - var id = message.Envelope.MessageId; - - if (string.IsNullOrEmpty (id)) - id = MimeUtils.GenerateMessageId (); - - if (ids.TryGetValue (id, out node)) { - if (node.Message == null) { - // a previously processed message referenced this message - node.Message = message; - } else { - // a duplicate message-id, just create a dummy id and use that - id = MimeUtils.GenerateMessageId (); - node = null; - } - } - - if (node == null) { - // create a new ThreadContainer for this message and add it to ids - node = new ThreadableNode (message); - ids.Add (id, node); - } - - ThreadableNode parent = null; - foreach (var reference in message.References) { - ThreadableNode referenced; - - if (!ids.TryGetValue (reference, out referenced)) { - // create a dummy container for the referenced message - referenced = new ThreadableNode (null); - ids.Add (reference, referenced); - } - - // chain up the references, disallowing loops - if (parent != null && referenced.Parent == null && parent != referenced && !parent.Children.Contains (referenced)) { - parent.Children.Add (referenced); - referenced.Parent = parent; - } - - parent = referenced; - } - - // don't allow loops - if (parent != null && (parent == node || node.Children.Contains (parent))) - parent = null; - - if (node.HasParent) { - // unlink from our old parent - node.Parent.Children.Remove (node); - node.Parent = null; - } - - if (parent != null) { - // add it as a child of our new parent - parent.Children.Add (node); - node.Parent = parent; - } - } - - return ids; - } - - static ThreadableNode CreateRoot (IDictionary ids) - { - var root = new ThreadableNode (null); - - foreach (var message in ids.Values) { - if (message.Parent == null) - root.Children.Add (message); - } - - return root; - } - - static void PruneEmptyContainers (ThreadableNode root) - { - for (int i = 0; i < root.Children.Count; i++) { - var node = root.Children[i]; - - if (node.Message == null && node.Children.Count == 0) { - // this is an empty container with no children, nuke it. - root.Children.RemoveAt (i); - i--; - } else if (node.Message == null && node.HasChildren && (node.HasParent || node.Children.Count == 1)) { - // If the Container has no Message, but does have children, remove this container but promote - // its children to this level (that is, splice them in to the current child list.) - // - // Do not promote the children if doing so would promote them to the root set -- unless there - // is only one child, in which case, do. - root.Children.RemoveAt (i); - - for (int j = 0; j < node.Children.Count; j++) { - node.Children[j].Parent = node.Parent; - root.Children.Add (node.Children[j]); - } - - node.Children.Clear (); - i--; - } else if (node.HasChildren) { - PruneEmptyContainers (node); - } - } - } - - static void GroupBySubject (ThreadableNode root) - { - var subjects = new Dictionary (StringComparer.OrdinalIgnoreCase); - ThreadableNode match; - int count = 0; - - for (int i = 0; i < root.Children.Count; i++) { - var current = root.Children[i]; - var subject = current.NormalizedSubject; - - // don't thread messages with empty subjects - if (string.IsNullOrEmpty (subject)) - continue; - - if (!subjects.TryGetValue (subject, out match) || - (current.Message == null && match.Message != null) || - (match.Message != null && match.Message.IsReply && - current.Message != null && !current.Message.IsReply)) { - subjects[subject] = current; - count++; - } - } - - if (count == 0) - return; - - for (int i = 0; i < root.Children.Count; i++) { - var current = root.Children[i]; - var subject = current.NormalizedSubject; - - // don't thread messages with empty subjects - if (string.IsNullOrEmpty (subject)) - continue; - - match = subjects[subject]; - - if (match == current) - continue; - - // remove the second message with the same subject - root.Children.RemoveAt (i--); - - // group these messages together... - if (match.Message == null && current.Message == null) { - // If both messages are dummies, append the current message's children - // to the children of the message in the subject table (the children of - // both messages become siblings), and then delete the current message. - match.Children.AddRange (current.Children); - } else if (match.Message == null && current.Message != null) { - // If the message in the subject table is a dummy and the current message - // 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) { - // 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). - match.Children.Add (current); - } else { - // Otherwise, create a new dummy message and make both the current message - // and the message in the subject table children of the dummy. Then replace - // the message in the subject table with the dummy message. - - // Note: if we re-use the node already in the subject table and the root, then - // we won't have to insert the new dummy node at the matched node's location - var dummy = match; - - // clone the message already in the subject table - match = new ThreadableNode (dummy.Message); - match.Children.AddRange (dummy.Children); - - // empty out the old match node (aka the new dummy node) - dummy.Children.Clear (); - dummy.Message = null; - - // now add both messages to the dummy - dummy.Children.Add (match); - dummy.Children.Add (current); - } - } - } - - static void GetThreads (ThreadableNode root, IList threads, IList orderBy) - { - root.Children.Sort (orderBy); - - for (int i = 0; i < root.Children.Count; i++) { - var message = root.Children[i].Message; - var thread = new MessageThread (message); - - GetThreads (root.Children[i], thread.Children, orderBy); - threads.Add (thread); - } - } - - static IList ThreadByReferences (IEnumerable messages, IList orderBy) - { - var threads = new List (); - var ids = CreateIdTable (messages); - var root = CreateRoot (ids); - - PruneEmptyContainers (root); - GroupBySubject (root); - - GetThreads (root, threads, orderBy); - - return threads; - } - - static IList ThreadBySubject (IEnumerable messages, IList orderBy) - { - var threads = new List (); - var root = new ThreadableNode (null); - - foreach (var message in messages) { - if (message.Envelope == null) - throw new ArgumentException ("One or more messages is missing information needed for threading.", nameof (messages)); - - var node = new ThreadableNode (message); - - root.Children.Add (node); - } - - GroupBySubject (root); - - GetThreads (root, threads, orderBy); - - return threads; - } - - /// - /// Thread the messages according to the specified threading algorithm. - /// - /// - /// Thread the messages according to the specified threading algorithm. - /// - /// The threaded messages. - /// The messages. - /// The threading algorithm. - /// - /// is null. - /// - /// - /// is not a valid threading algorithm. - /// - /// - /// contains one or more items that is missing information needed for threading. - /// - public static IList Thread (this IEnumerable messages, ThreadingAlgorithm algorithm) - { - return Thread (messages, algorithm, new [] { OrderBy.Arrival }); - } - - /// - /// Threads the messages according to the specified threading algorithm - /// and sorts the resulting threads by the specified ordering. - /// - /// - /// Threads the messages according to the specified threading algorithm - /// and sorts the resulting threads by the specified ordering. - /// - /// The threaded messages. - /// The messages. - /// The threading algorithm. - /// The requested sort ordering. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid threading algorithm. - /// - /// - /// contains one or more items that is missing information needed for threading or sorting. - /// -or- - /// is an empty list. - /// - public static IList Thread (this IEnumerable messages, ThreadingAlgorithm algorithm, IList orderBy) - { - if (messages == null) - throw new ArgumentNullException (nameof (messages)); - - if (orderBy == null) - throw new ArgumentNullException (nameof (orderBy)); - - if (orderBy.Count == 0) - throw new ArgumentException ("No sort order provided.", nameof (orderBy)); - - switch (algorithm) { - case ThreadingAlgorithm.OrderedSubject: return ThreadBySubject (messages, orderBy); - case ThreadingAlgorithm.References: return ThreadByReferences (messages, orderBy); - default: throw new ArgumentOutOfRangeException (nameof (algorithm)); - } - } - - static bool IsForward (string subject, int index) - { - return (subject[index] == 'F' || subject[index] == 'f') && - (subject[index + 1] == 'W' || subject[index + 1] == 'w') && - (subject[index + 2] == 'D' || subject[index + 2] == 'd') && - subject[index + 3] == ':'; - } - - static bool IsReply (string subject, int index) - { - return (subject[index] == 'R' || subject[index] == 'r') && - (subject[index + 1] == 'E' || subject[index + 1] == 'e'); - } - - static void SkipWhiteSpace (string subject, ref int index) - { - while (index < subject.Length && char.IsWhiteSpace (subject[index])) - index++; - } - - static bool IsMailingListName (char c) - { - return c == '-' || c == '_' || char.IsLetterOrDigit (c); - } - - static void SkipMailingListName (string subject, ref int index) - { - while (index < subject.Length && IsMailingListName (subject[index])) - index++; - } - - static bool SkipDigits (string subject, ref int index, out int value) - { - int startIndex = index; - - value = 0; - - while (index < subject.Length && char.IsDigit (subject[index])) { - value = (value * 10) + (subject[index] - '0'); - index++; - } - - return index > startIndex; - } - - /// - /// Gets the threadable subject. - /// - /// - /// Gets the threadable subject. - /// - /// The threadable subject. - /// The Subject header value. - /// The reply depth. - /// - /// is null. - /// - public static string GetThreadableSubject (string subject, out int replyDepth) - { - if (subject == null) - throw new ArgumentNullException (nameof (subject)); - - replyDepth = 0; - - int endIndex = subject.Length; - int startIndex = 0; - int index, count; - int left; - - do { - SkipWhiteSpace (subject, ref startIndex); - index = startIndex; - - if ((left = (endIndex - index)) < 3) - break; - - if (left >= 4 && IsForward (subject, index)) { - // skip over the "Fwd:" prefix - startIndex = index + 4; - replyDepth++; - continue; - } - - if (IsReply (subject, index)) { - if (subject[index + 2] == ':') { - // skip over the "Re:" prefix - startIndex = index + 3; - replyDepth++; - continue; - } - - if (subject[index + 2] == '[' || subject[index + 2] == '(') { - char close = subject[index + 2] == '[' ? ']' : ')'; - - // skip over "Re[" or "Re(" - index += 3; - - // if this is followed by "###]:" or "###):", then it's a condensed "Re:" - if (SkipDigits (subject, ref index, out count) && (endIndex - index) >= 2 && - subject[index] == close && subject[index + 1] == ':') { - startIndex = index + 2; - replyDepth += count; - continue; - } - } - } else if (subject[index] == '[' && char.IsLetterOrDigit (subject[index + 1])) { - // possibly a mailing-list prefix - index += 2; - - SkipMailingListName (subject, ref index); - - if ((endIndex - index) >= 1 && subject[index] == ']') { - startIndex = index + 1; - continue; - } - } - - break; - } while (true); - - // trim trailing whitespace - while (endIndex > 0 && char.IsWhiteSpace (subject[endIndex - 1])) - endIndex--; - - // canonicalize the remainder of the subject, condensing multiple spaces into 1 - var builder = new StringBuilder (); - bool lwsp = false; - - for (int i = startIndex; i < endIndex; i++) { - if (char.IsWhiteSpace (subject[i])) { - if (!lwsp) { - builder.Append (' '); - lwsp = true; - } - } else { - builder.Append (subject[i]); - lwsp = false; - } - } - - var canonicalized = builder.ToString (); - - if (canonicalized.Equals ("(no subject)", StringComparison.OrdinalIgnoreCase)) - canonicalized = string.Empty; - - return canonicalized; - } - } -} diff --git a/src/MailKit/MessagesVanishedEventArgs.cs b/src/MailKit/MessagesVanishedEventArgs.cs deleted file mode 100644 index 61b8326..0000000 --- a/src/MailKit/MessagesVanishedEventArgs.cs +++ /dev/null @@ -1,79 +0,0 @@ -// -// MessagesVanishedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace MailKit { - /// - /// Event args used when a message vanishes from a folder. - /// - /// - /// Event args used when a message vanishes from a folder. - /// - public class MessagesVanishedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The list of unique identifiers. - /// If set to true, the messages vanished in the past as opposed to just now. - /// - /// is null. - /// - public MessagesVanishedEventArgs (IList uids, bool earlier) - { - UniqueIds = new ReadOnlyCollection (uids); - Earlier = earlier; - } - - /// - /// Gets the unique identifiers of the messages that vanished. - /// - /// - /// Gets the unique identifiers of the messages that vanished. - /// - /// The unique identifiers. - public IList UniqueIds { - get; private set; - } - - /// - /// Gets whether the messages vanished in the past as opposed to just now. - /// - /// - /// Gets whether the messages vanished in the past as opposed to just now. - /// - /// true if the messages vanished earlier; otherwise, false. - public bool Earlier { - get; private set; - } - } -} diff --git a/src/MailKit/Metadata.cs b/src/MailKit/Metadata.cs deleted file mode 100644 index 03d24e6..0000000 --- a/src/MailKit/Metadata.cs +++ /dev/null @@ -1,74 +0,0 @@ -// -// Metadata.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit { - /// - /// A metadata tag and value. - /// - /// - /// A metadata tag and value. - /// - public class Metadata - { - internal string EncodedName; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The metadata tag. - /// The metadata value. - public Metadata (MetadataTag tag, string value) - { - Value = value; - Tag = tag; - } - - /// - /// Gets the metadata tag. - /// - /// - /// Gets the metadata tag. - /// - /// The metadata tag. - public MetadataTag Tag { - get; private set; - } - - /// - /// Gets the metadata value. - /// - /// - /// Gets the metadata value. - /// - /// The metadata value. - public string Value { - get; private set; - } - } -} diff --git a/src/MailKit/MetadataChangedEventArgs.cs b/src/MailKit/MetadataChangedEventArgs.cs deleted file mode 100644 index f07ae0e..0000000 --- a/src/MailKit/MetadataChangedEventArgs.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// MetadataChangedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Event args used when a metadata changes. - /// - /// - /// Event args used when a metadata changes. - /// - public class MetadataChangedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The metadata that changed. - /// - /// is null. - /// - public MetadataChangedEventArgs (Metadata metadata) - { - if (metadata == null) - throw new ArgumentNullException (nameof (metadata)); - - Metadata = metadata; - } - - /// - /// Get the metadata that changed. - /// - /// - /// Gets the metadata that changed. - /// - /// The metadata. - public Metadata Metadata { - get; private set; - } - } -} diff --git a/src/MailKit/MetadataCollection.cs b/src/MailKit/MetadataCollection.cs deleted file mode 100644 index 20c7651..0000000 --- a/src/MailKit/MetadataCollection.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// MetadataCollection.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Collections.Generic; - -namespace MailKit { - /// - /// A collection of metadata. - /// - /// - /// A collection of metadata. - /// - public class MetadataCollection : List - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new - /// - public MetadataCollection () - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// A collection of metadata. - public MetadataCollection (IEnumerable collection) : base (collection) - { - } - } -} diff --git a/src/MailKit/MetadataOptions.cs b/src/MailKit/MetadataOptions.cs deleted file mode 100644 index 77735bf..0000000 --- a/src/MailKit/MetadataOptions.cs +++ /dev/null @@ -1,108 +0,0 @@ -// -// MetadataOptions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit -{ - /// - /// A set of options to use when requesting metadata. - /// - /// - /// A set of options to use when requesting metadata. - /// - public class MetadataOptions - { - int depth; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new set of options to use when requesting metadata. - /// - public MetadataOptions () - { - } - - /// - /// Get or set the depth. - /// - /// - /// When the option is specified, it extends the list of metadata tag - /// values returned by the GetMetadata() call. For each specified in the - /// the GetMetadata() call, the method returns the value of the specified metadata tag (if it exists), - /// plus all metadata tags below the specified entry up to the specified depth. - /// Three values are allowed for : - /// 0 - no entries below the specified metadata tag are returned. - /// 1 - only entries immediately below the specified metadata tag are returned. - /// - all entries below the specified metadata tag are returned. - /// Thus, a depth of 1 for a tag entry of "/a" will match "/a" as well as its children - /// entries (e.g., "/a/b"), but will not match grandchildren entries (e.g., "/a/b/c"). - /// If the Depth option is not specified, this is the same as specifying 0. - /// - /// The depth. - /// - /// is out of range. - /// - public int Depth { - get { return depth; } - set { - if (!(value == 0 || value == 1 || value == int.MaxValue)) - throw new ArgumentOutOfRangeException (nameof (value)); - - depth = value; - } - } - - /// - /// Get or set the max size of the metadata tags to request. - /// - /// - /// When specified, the property is used to filter the metadata tags - /// returned by the GetMetadata() call to only those with a value shorter than the max size - /// specified. - /// - /// The size of the max. - public uint? MaxSize { - get; set; - } - - /// - /// Get the length of the longest metadata value. - /// - /// - /// If the property is specified, once the GetMetadata() call returns, - /// the property will be set to the length of the longest metadata - /// value that exceeded the limit, otherwise a value of 0 will - /// be set. - /// - /// The length of the longest metadata value that exceeded the max size. - public uint LongEntries { - get; set; - } - } -} diff --git a/src/MailKit/MetadataTag.cs b/src/MailKit/MetadataTag.cs deleted file mode 100644 index 624da0d..0000000 --- a/src/MailKit/MetadataTag.cs +++ /dev/null @@ -1,156 +0,0 @@ -// -// MetadataTag.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// A metadata tag. - /// - /// - /// A metadata tag. - /// - public struct MetadataTag - { - /// - /// A metadata tag for specifying the contact information for the server administrator. - /// - /// - /// Used to get the contact information of the administrator on a - /// . - /// - public static readonly MetadataTag SharedAdmin = new MetadataTag ("/shared/admin"); - - /// - /// A metadata tag for private comments. - /// - /// - /// Used to get or set a private comment on a . - /// - public static readonly MetadataTag PrivateComment = new MetadataTag ("/private/comment"); - - /// - /// A metadata tag for shared comments. - /// - /// - /// Used to get or set a shared comment on a - /// or . - /// - public static readonly MetadataTag SharedComment = new MetadataTag ("/shared/comment"); - - /// - /// A metadata tag for specifying the special use of a folder. - /// - /// - /// Used to get or set the special use of a . - /// - public static readonly MetadataTag PrivateSpecialUse = new MetadataTag ("/private/specialuse"); - - /// - /// Initializes a new instance of the struct. - /// - /// - /// Creates a new . - /// - /// The metadata tag identifier. - /// - /// is null. - /// - /// - /// is an empty string. - /// - public MetadataTag (string id) - { - if (id == null) - throw new ArgumentNullException (nameof (id)); - - if (id.Length == 0) - throw new ArgumentException ("A metadata tag identifier cannot be empty."); - - Id = id; - } - - /// - /// Get the metadata tag identifier. - /// - /// - /// Gets the metadata tag identifier. - /// - /// The metadata tag identifier. - public string Id { - get; private set; - } - - /// - /// Determine whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public override bool Equals (object obj) - { - return obj is MetadataTag && ((MetadataTag) obj).Id == Id; - } - - /// - /// Serves as a hash function for a object. - /// - /// - /// Serves as a hash function for a object. - /// - /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a hash table. - public override int GetHashCode () - { - return Id.GetHashCode (); - } - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - return Id; - } - - internal static MetadataTag Create (string id) - { - switch (id) { - case "/shared/admin": return SharedAdmin; - case "/private/comment": return PrivateComment; - case "/shared/comment": return SharedComment; - case "/private/specialuse": return PrivateSpecialUse; - default: return new MetadataTag (id); - } - } - } -} diff --git a/src/MailKit/ModSeqChangedEventArgs.cs b/src/MailKit/ModSeqChangedEventArgs.cs deleted file mode 100644 index 3aaca32..0000000 --- a/src/MailKit/ModSeqChangedEventArgs.cs +++ /dev/null @@ -1,86 +0,0 @@ -// -// ModSeqChangedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit -{ - /// - /// Event args for the event. - /// - /// - /// Event args for the event. - /// - 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. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The modification sequence value. - public ModSeqChangedEventArgs (int index, ulong modseq) : base (index) - { - ModSeq = modseq; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message index. - /// The unique id of the message. - /// The modification sequence value. - public ModSeqChangedEventArgs (int index, UniqueId uid, ulong modseq) : base (index, uid) - { - ModSeq = modseq; - } - - /// - /// Gets the updated mod-sequence value of the message. - /// - /// - /// Gets the updated mod-sequence value of the message. - /// - /// The mod-sequence value. - public ulong ModSeq { - get; internal set; - } - } -} diff --git a/src/MailKit/Net/Imap/AsyncImapClient.cs b/src/MailKit/Net/Imap/AsyncImapClient.cs deleted file mode 100644 index b88d25d..0000000 --- a/src/MailKit/Net/Imap/AsyncImapClient.cs +++ /dev/null @@ -1,965 +0,0 @@ -// -// AsyncImapClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MailKit.Security; - -namespace MailKit.Net.Imap -{ - public partial class ImapClient - { - /// - /// Asynchronously enable compression over the IMAP connection. - /// - /// - /// Asynchronously enables compression over the IMAP connection. - /// If the IMAP server supports the extension, - /// it is possible at any point after connecting to enable compression to reduce network - /// bandwidth usage. Ideally, this method should be called before authenticating. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Compression must be enabled before a folder has been selected. - /// - /// - /// The IMAP server does not support the COMPRESS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the COMPRESS command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - public Task CompressAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return CompressAsync (true, cancellationToken); - } - - /// - /// Asynchronously enable the QRESYNC feature. - /// - /// - /// Enables the QRESYNC feature. - /// The QRESYNC extension improves resynchronization performance of folders by - /// querying the IMAP server for a list of changes when the folder is opened using the - /// - /// method. - /// If this feature is enabled, the event is replaced - /// with the event. - /// This method needs to be called immediately after calling one of the - /// Authenticate methods, before - /// opening any folders. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Quick resynchronization needs to be enabled before selecting a folder. - /// - /// - /// The IMAP server does not support the QRESYNC extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ENABLE command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - public override Task EnableQuickResyncAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return EnableQuickResyncAsync (true, cancellationToken); - } - - /// - /// Asynchronously enable the UTF8=ACCEPT extension. - /// - /// - /// Enables the UTF8=ACCEPT extension. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// UTF8=ACCEPT needs to be enabled before selecting a folder. - /// - /// - /// The IMAP server does not support the UTF8=ACCEPT extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ENABLE command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - public Task EnableUTF8Async (CancellationToken cancellationToken = default (CancellationToken)) - { - return EnableUTF8Async (true, cancellationToken); - } - - /// - /// Asynchronously identify the client implementation to the server and obtain the server implementation details. - /// - /// - /// Passes along the client implementation details to the server while also obtaining implementation - /// details from the server. - /// If the is null or no properties have been set, no - /// identifying information will be sent to the server. - /// - /// Security Implications - /// This command has the danger of violating the privacy of users if misused. Clients should - /// notify users that they send the ID command. - /// It is highly desirable that implementations provide a method of disabling ID support, perhaps by - /// not calling this method at all, or by passing null as the - /// argument. - /// Implementors must exercise extreme care in adding properties to the . - /// Some properties, such as a processor ID number, Ethernet address, or other unique (or mostly unique) identifier - /// would allow tracking of users in ways that violate user privacy expectations and may also make it easier for - /// attackers to exploit security holes in the client. - /// - /// - /// The implementation details of the server if available; otherwise, null. - /// The client implementation. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The IMAP server does not support the ID extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ID command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - public Task IdentifyAsync (ImapImplementation clientImplementation, CancellationToken cancellationToken = default (CancellationToken)) - { - return IdentifyAsync (clientImplementation, true, cancellationToken); - } - - /// - /// Asynchronously authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// An asynchronous task context. - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override Task AuthenticateAsync (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)) - { - return AuthenticateAsync (mechanism, true, cancellationToken); - } - - /// - /// Asynchronously authenticate using the supplied credentials. - /// - /// - /// If the IMAP server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then LOGIN command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// An asynchronous task context. - /// The text encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override Task AuthenticateAsync (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)) - { - return AuthenticateAsync (encoding, credentials, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified IMAP server. - /// - /// - /// Establishes a connection to the specified IMAP or IMAP/S server. - /// If the has a value of 0, then the - /// parameter is used to determine the default port to - /// connect to. The default port used with - /// is 993. All other values will use a default port of 143. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 993, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// - /// - /// - /// - /// An asynchronous task context. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the IMAP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override Task ConnectAsync (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified IMAP or IMAP/S server using the provided socket. - /// - /// - /// Establishes a connection to the specified IMAP or IMAP/S server using - /// the provided socket. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 993, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// An asynchronous task context. - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the IMAP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override Task ConnectAsync (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (socket, host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified IMAP or IMAP/S server using the provided stream. - /// - /// - /// Establishes a connection to the specified IMAP or IMAP/S server using - /// the provided stream. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 993, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// An asynchronous task context. - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the IMAP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override Task ConnectAsync (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (stream, host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously disconnect the service. - /// - /// - /// If is true, a LOGOUT command will be issued in order to disconnect cleanly. - /// - /// - /// - /// - /// An asynchronous task context. - /// If set to true, a LOGOUT command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - public override Task DisconnectAsync (bool quit, CancellationToken cancellationToken = default (CancellationToken)) - { - return DisconnectAsync (quit, true, cancellationToken); - } - - /// - /// Asynchronously ping the IMAP server to keep the connection alive. - /// - /// - /// The NOOP command is typically used to keep the connection with the IMAP server - /// alive. When a client goes too long (typically 30 minutes) without sending any commands to the - /// IMAP server, the IMAP server will close the connection with the client, forcing the client to - /// reconnect before it can send any more commands. - /// The NOOP command also provides a great way for a client to check for new - /// messages. - /// When the IMAP server receives a NOOP command, it will reply to the client with a - /// list of pending updates such as EXISTS and RECENT counts on the currently - /// selected folder. To receive these notifications, subscribe to the - /// and events, - /// respectively. - /// For more information about the NOOP command, see - /// rfc3501. - /// - /// - /// - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOOP command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public override Task NoOpAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return NoOpAsync (true, cancellationToken); - } - - /// - /// Asynchronously toggle the into the IDLE state. - /// - /// - /// When a client enters the IDLE state, the IMAP server will send - /// events to the client as they occur on the selected folder. These events - /// may include notifications of new messages arriving, expunge notifications, - /// flag changes, etc. - /// Due to the nature of the IDLE command, a folder must be selected - /// before a client can enter into the IDLE state. This can be done by - /// opening a folder using - /// - /// or any of the other variants. - /// While the IDLE command is running, no other commands may be issued until the - /// is cancelled. - /// It is especially important to cancel the - /// before cancelling the when using SSL or TLS due to - /// the fact that cannot be polled. - /// - /// An asynchronous task context. - /// The cancellation token used to return to the non-idle state. - /// The cancellation token. - /// - /// must be cancellable (i.e. cannot be used). - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// A has not been opened. - /// - /// - /// The IMAP server does not support the IDLE extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the IDLE command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public Task IdleAsync (CancellationToken doneToken, CancellationToken cancellationToken = default (CancellationToken)) - { - return IdleAsync (doneToken, true, cancellationToken); - } - - /// - /// Asynchronously request the specified notification events from the IMAP server. - /// - /// - /// The NOTIFY command is used to expand - /// which notifications the client wishes to be notified about, including status notifications - /// about folders other than the currently selected folder. It can also be used to automatically - /// FETCH information about new messages that have arrived in the currently selected folder. - /// This, combined with , - /// can be used to get instant notifications for changes to any of the specified folders. - /// - /// An asynchronous task context. - /// true if the server should immediately notify the client of the - /// selected folder's status; otherwise, false. - /// The specific event groups that the client would like to receive notifications for. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// One or more is invalid. - /// - /// - /// The IMAP server does not support the NOTIFY extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOTIFY command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public Task NotifyAsync (bool status, IList eventGroups, CancellationToken cancellationToken = default (CancellationToken)) - { - return NotifyAsync (status, eventGroups, true, cancellationToken); - } - - /// - /// Asynchronously disable any previously requested notification events from the IMAP server. - /// - /// - /// Disables any notification events requested in a prior call to - /// . - /// request. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the NOTIFY extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOTIFY command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public Task DisableNotifyAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return DisableNotifyAsync (true, cancellationToken); - } - - /// - /// Asynchronously get all of the folders within the specified namespace. - /// - /// - /// Gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The namespace folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the LIST or LSUB command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public override Task> GetFoldersAsync (FolderNamespace @namespace, StatusItems items = StatusItems.None, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetFoldersAsync (@namespace, items, subscribedOnly, true, cancellationToken); - } - - /// - /// Asynchronously get the folder for the specified path. - /// - /// - /// Gets the folder for the specified path. - /// - /// The folder. - /// The folder path. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the IDLE command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public override async Task GetFolderAsync (string path, CancellationToken cancellationToken = default (CancellationToken)) - { - if (path == null) - throw new ArgumentNullException (nameof (path)); - - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - return await engine.GetFolderAsync (path, true, cancellationToken).ConfigureAwait (false); - } - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA or METADATA-SERVER extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (tag, true, cancellationToken); - } - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA or METADATA-SERVER extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetMetadataAsync (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (options, tags, true, cancellationToken); - } - - /// - /// Asynchronously gets the specified metadata. - /// - /// - /// Sets the specified metadata. - /// - /// An asynchronous task context. - /// The metadata. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA or METADATA-SERVER extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SetMetadataAsync (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetMetadataAsync (metadata, true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Imap/IImapClient.cs b/src/MailKit/Net/Imap/IImapClient.cs deleted file mode 100644 index a9b693d..0000000 --- a/src/MailKit/Net/Imap/IImapClient.cs +++ /dev/null @@ -1,628 +0,0 @@ -// -// IImapClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MailKit.Net.Imap { - /// - /// An interface for an IMAP client. - /// - /// - /// Implemented by . - /// - public interface IImapClient : IMailStore - { - /// - /// Get the capabilities supported by the IMAP server. - /// - /// - /// The capabilities will not be known until a successful connection has been made via one of - /// the Connect methods and may - /// change as a side-effect of calling one of the - /// Authenticate - /// methods. - /// - /// - /// - /// - /// The capabilities. - /// - /// Capabilities cannot be enabled, they may only be disabled. - /// - ImapCapabilities Capabilities { get; set; } - - /// - /// Gets the maximum size of a message that can be appended to a folder. - /// - /// - /// Gets the maximum size of a message, in bytes, that can be appended to a folder. - /// If the value is not set, then the limit is unspecified. - /// - /// The append limit. - uint? AppendLimit { get; } - - /// - /// Gets the internationalization level supported by the IMAP server. - /// - /// - /// Gets the internationalization level supported by the IMAP server. - /// For more information, see - /// section 4 of rfc5255. - /// - /// The internationalization level. - int InternationalizationLevel { get; } - - /// - /// Get the access rights supported by the IMAP server. - /// - /// - /// These rights are additional rights supported by the IMAP server beyond the standard rights - /// defined in section 2.1 of rfc4314 - /// and will not be populated until the client is successfully connected. - /// - /// - /// - /// - /// The rights. - AccessRights Rights { get; } - - /// - /// Get whether or not the client is currently in the IDLE state. - /// - /// - /// Gets whether or not the client is currently in the IDLE state. - /// - /// true if an IDLE command is active; otherwise, false. - bool IsIdle { get; } - - /// - /// Enable compression over the IMAP connection. - /// - /// - /// Enables compression over the IMAP connection. - /// If the IMAP server supports the extension, - /// it is possible at any point after connecting to enable compression to reduce network - /// bandwidth usage. Ideally, this method should be called before authenticating. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Compression must be enabled before a folder has been selected. - /// - /// - /// The IMAP server does not support the extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the COMPRESS command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - void Compress (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously enable compression over the IMAP connection. - /// - /// - /// Asynchronously enables compression over the IMAP connection. - /// If the IMAP server supports the extension, - /// it is possible at any point after connecting to enable compression to reduce network - /// bandwidth usage. Ideally, this method should be called before authenticating. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Compression must be enabled before a folder has been selected. - /// - /// - /// The IMAP server does not support the COMPRESS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the COMPRESS command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - Task CompressAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Enable the UTF8=ACCEPT extension. - /// - /// - /// Enables the UTF8=ACCEPT extension. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// UTF8=ACCEPT needs to be enabled before selecting a folder. - /// - /// - /// The IMAP server does not support the UTF8=ACCEPT extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ENABLE command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - void EnableUTF8 (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously enable the UTF8=ACCEPT extension. - /// - /// - /// Enables the UTF8=ACCEPT extension. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// UTF8=ACCEPT needs to be enabled before selecting a folder. - /// - /// - /// The IMAP server does not support the UTF8=ACCEPT extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ENABLE command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - Task EnableUTF8Async (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Identify the client implementation to the server and obtain the server implementation details. - /// - /// - /// Passes along the client implementation details to the server while also obtaining implementation - /// details from the server. - /// If the is null or no properties have been set, no - /// identifying information will be sent to the server. - /// - /// Security Implications - /// This command has the danger of violating the privacy of users if misused. Clients should - /// notify users that they send the ID command. - /// It is highly desirable that implementations provide a method of disabling ID support, perhaps by - /// not calling this method at all, or by passing null as the - /// argument. - /// Implementors must exercise extreme care in adding properties to the . - /// Some properties, such as a processor ID number, Ethernet address, or other unique (or mostly unique) identifier - /// would allow tracking of users in ways that violate user privacy expectations and may also make it easier for - /// attackers to exploit security holes in the client. - /// - /// - /// - /// - /// - /// The implementation details of the server if available; otherwise, null. - /// The client implementation. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The IMAP server does not support the ID extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ID command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - ImapImplementation Identify (ImapImplementation clientImplementation, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously identify the client implementation to the server and obtain the server implementation details. - /// - /// - /// Passes along the client implementation details to the server while also obtaining implementation - /// details from the server. - /// If the is null or no properties have been set, no - /// identifying information will be sent to the server. - /// - /// Security Implications - /// This command has the danger of violating the privacy of users if misused. Clients should - /// notify users that they send the ID command. - /// It is highly desirable that implementations provide a method of disabling ID support, perhaps by - /// not calling this method at all, or by passing null as the - /// argument. - /// Implementors must exercise extreme care in adding properties to the . - /// Some properties, such as a processor ID number, Ethernet address, or other unique (or mostly unique) identifier - /// would allow tracking of users in ways that violate user privacy expectations and may also make it easier for - /// attackers to exploit security holes in the client. - /// - /// - /// The implementation details of the server if available; otherwise, null. - /// The client implementation. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The IMAP server does not support the ID extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ID command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - Task IdentifyAsync (ImapImplementation clientImplementation, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Toggle the into the IDLE state. - /// - /// - /// When a client enters the IDLE state, the IMAP server will send - /// events to the client as they occur on the selected folder. These events - /// may include notifications of new messages arriving, expunge notifications, - /// flag changes, etc. - /// Due to the nature of the IDLE command, a folder must be selected - /// before a client can enter into the IDLE state. This can be done by - /// opening a folder using - /// - /// or any of the other variants. - /// While the IDLE command is running, no other commands may be issued until the - /// is cancelled. - /// It is especially important to cancel the - /// before cancelling the when using SSL or TLS due to - /// the fact that cannot be polled. - /// - /// - /// - /// - /// The cancellation token used to return to the non-idle state. - /// The cancellation token. - /// - /// must be cancellable (i.e. cannot be used). - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// A has not been opened. - /// - /// - /// The IMAP server does not support the IDLE extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the IDLE command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - void Idle (CancellationToken doneToken, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously toggle the into the IDLE state. - /// - /// - /// When a client enters the IDLE state, the IMAP server will send - /// events to the client as they occur on the selected folder. These events - /// may include notifications of new messages arriving, expunge notifications, - /// flag changes, etc. - /// Due to the nature of the IDLE command, a folder must be selected - /// before a client can enter into the IDLE state. This can be done by - /// opening a folder using - /// - /// or any of the other variants. - /// While the IDLE command is running, no other commands may be issued until the - /// is cancelled. - /// It is especially important to cancel the - /// before cancelling the when using SSL or TLS due to - /// the fact that cannot be polled. - /// - /// An asynchronous task context. - /// The cancellation token used to return to the non-idle state. - /// The cancellation token. - /// - /// must be cancellable (i.e. cannot be used). - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// A has not been opened. - /// - /// - /// The IMAP server does not support the IDLE extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the IDLE command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - Task IdleAsync (CancellationToken doneToken, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Request the specified notification events from the IMAP server. - /// - /// - /// The NOTIFY command is used to expand - /// which notifications the client wishes to be notified about, including status notifications - /// about folders other than the currently selected folder. It can also be used to automatically - /// FETCH information about new messages that have arrived in the currently selected folder. - /// This, combined with , - /// can be used to get instant notifications for changes to any of the specified folders. - /// - /// true if the server should immediately notify the client of the - /// selected folder's status; otherwise, false. - /// The specific event groups that the client would like to receive notifications for. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// One or more is invalid. - /// - /// - /// The IMAP server does not support the NOTIFY extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOTIFY command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - void Notify (bool status, IList eventGroups, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously request the specified notification events from the IMAP server. - /// - /// - /// The NOTIFY command is used to expand - /// which notifications the client wishes to be notified about, including status notifications - /// about folders other than the currently selected folder. It can also be used to automatically - /// FETCH information about new messages that have arrived in the currently selected folder. - /// This, combined with , - /// can be used to get instant notifications for changes to any of the specified folders. - /// - /// An asynchronous task context. - /// true if the server should immediately notify the client of the - /// selected folder's status; otherwise, false. - /// The specific event groups that the client would like to receive notifications for. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// One or more is invalid. - /// - /// - /// The IMAP server does not support the NOTIFY extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOTIFY command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - Task NotifyAsync (bool status, IList eventGroups, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Disable any previously requested notification events from the IMAP server. - /// - /// - /// Disables any notification events requested in a prior call to - /// . - /// request. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the NOTIFY extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOTIFY command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - void DisableNotify (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously disable any previously requested notification events from the IMAP server. - /// - /// - /// Disables any notification events requested in a prior call to - /// . - /// request. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the NOTIFY extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOTIFY command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - Task DisableNotifyAsync (CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MailKit/Net/Imap/IImapFolder.cs b/src/MailKit/Net/Imap/IImapFolder.cs deleted file mode 100644 index 2006d6b..0000000 --- a/src/MailKit/Net/Imap/IImapFolder.cs +++ /dev/null @@ -1,869 +0,0 @@ -// -// IImapFolder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -using MailKit.Search; - -namespace MailKit.Net.Imap { - /// - /// An interface for an IMAP folder. - /// - /// - /// Implemented by . - /// - /// - /// - /// - /// - /// - /// - public interface IImapFolder : IMailFolder - { - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - HeaderList GetHeaders (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task GetHeadersAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - HeaderList GetHeaders (int index, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task GetHeadersAsync (int index, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The UID of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - MimeEntity GetBodyPart (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The UID of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task GetBodyPartAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - MimeEntity GetBodyPart (int index, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task GetBodyPartAsync (int index, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the streams for the specified messages. - /// - /// - /// Gets the streams for the specified messages. - /// - /// The uids of the messages. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - void GetStreams (IList uids, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the streams for the specified messages. - /// - /// - /// Asynchronously gets the streams for the specified messages. - /// - /// An awaitable task. - /// The uids of the messages. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task GetStreamsAsync (IList uids, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the streams for the specified messages. - /// - /// - /// Gets the streams for the specified messages. - /// - /// The indexes of the messages. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - void GetStreams (IList indexes, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the streams for the specified messages. - /// - /// - /// Asynchronously gets the streams for the specified messages. - /// - /// An awaitable task. - /// The indexes of the messages. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task GetStreamsAsync (IList indexes, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Get the streams for the specified messages. - /// - /// - /// Gets the streams for the specified messages. - /// - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - void GetStreams (int min, int max, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Asynchronously get the streams for the specified messages. - /// - /// - /// Asynchronously gets the streams for the specified messages. - /// - /// An awaitable task. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task GetStreamsAsync (int min, int max, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null); - - /// - /// Search the folder for messages matching the specified query. - /// - /// - /// Sends a UID SEARCH command with the specified query passed directly to the IMAP server - /// with no interpretation by MailKit. This means that the query may contain any arguments that a - /// UID SEARCH command is allowed to have according to the IMAP specifications and any - /// extensions that are supported, including RETURN parameters. - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - SearchResults Search (string query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously search the folder for messages matching the specified query. - /// - /// - /// Sends a UID SEARCH command with the specified query passed directly to the IMAP server - /// with no interpretation by MailKit. This means that the query may contain any arguments that a - /// UID SEARCH command is allowed to have according to the IMAP specifications and any - /// extensions that are supported, including RETURN parameters. - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task SearchAsync (string query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Sort messages matching the specified query. - /// - /// - /// Sends a UID SORT command with the specified query passed directly to the IMAP server - /// with no interpretation by MailKit. This means that the query may contain any arguments that a - /// UID SORT command is allowed to have according to the IMAP specifications and any - /// extensions that are supported, including RETURN parameters. - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The IMAP server does not support the SORT extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - SearchResults Sort (string query, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// Sends a UID SORT command with the specified query passed directly to the IMAP server - /// with no interpretation by MailKit. This means that the query may contain any arguments that a - /// UID SORT command is allowed to have according to the IMAP specifications and any - /// extensions that are supported, including RETURN parameters. - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The IMAP server does not support the SORT extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - Task SortAsync (string query, CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MailKit/Net/Imap/ImapCallbacks.cs b/src/MailKit/Net/Imap/ImapCallbacks.cs deleted file mode 100644 index 3ba97cb..0000000 --- a/src/MailKit/Net/Imap/ImapCallbacks.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// ImapCallbacks.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MailKit.Net.Imap -{ - /// - /// A callback used when fetching message streams. - /// - /// - /// This callback is meant to be used with the various - /// GetStreams - /// methods. - /// Once this callback returns, the stream argument will be disposed, so - /// it is important to consume the stream right away and not add it to a queue - /// for later processing. - /// - /// The IMAP folder that the message belongs to. - /// The index of the message in the folder. - /// The UID of the message in the folder. - /// The raw message (or part) stream. - public delegate void ImapFetchStreamCallback (ImapFolder folder, int index, UniqueId uid, Stream stream); - - /// - /// An asynchronous callback used when fetching message streams. - /// - /// - /// This callback is meant to be used with the various - /// GetStreamsAsync - /// methods. - /// Once this callback returns, the stream argument will be disposed, so - /// it is important to consume the stream right away and not add it to a queue - /// for later processing. - /// - /// An awaitable task context. - /// The IMAP folder that the message belongs to. - /// The index of the message in the folder. - /// The UID of the message in the folder. - /// The raw message (or part) stream. - /// The cancellation token. - public delegate Task ImapFetchStreamAsyncCallback (ImapFolder folder, int index, UniqueId uid, Stream stream, CancellationToken cancellationToken); -} diff --git a/src/MailKit/Net/Imap/ImapCapabilities.cs b/src/MailKit/Net/Imap/ImapCapabilities.cs deleted file mode 100644 index a27b51b..0000000 --- a/src/MailKit/Net/Imap/ImapCapabilities.cs +++ /dev/null @@ -1,348 +0,0 @@ -// -// ImapCapabilities.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Net.Imap { - /// - /// Capabilities supported by an IMAP server. - /// - /// - /// Capabilities are read as part of the response to the CAPABILITY command that - /// is issued during the connection and authentication phases of the - /// . - /// - /// - /// - /// - [Flags] - public enum ImapCapabilities : ulong { - /// - /// The server does not support any additional extensions. - /// - None = 0, - - /// - /// The server implements the core IMAP4 commands. - /// - IMAP4 = 1L << 0, - - /// - /// The server implements the core IMAP4rev1 commands. - /// - IMAP4rev1 = 1L << 1, - - /// - /// The server implements the core IMAP4rev2 commands. - /// - IMAP4rev2 = 1L << 2, - - /// - /// The server supports the STATUS command. - /// - Status = 1L << 3, - - /// - /// The server supports the ACL extension defined in rfc2086 - /// and rfc4314. - /// - Acl = 1L << 4, - - /// - /// The server supports the QUOTA extension. - /// - Quota = 1L << 5, - - /// - /// The server supports the LITERAL+ extension. - /// - LiteralPlus = 1L << 6, - - /// - /// The server supports the IDLE extension. - /// - Idle = 1L << 7, - - /// - /// The server supports the MAILBOX-REFERRALS extension. - /// - MailboxReferrals = 1L << 8, - - /// - /// the server supports the LOGIN-REFERRALS extension. - /// - LoginReferrals = 1L << 9, - - /// - /// The server supports the NAMESPACE extension. - /// - Namespace = 1L << 10, - - /// - /// The server supports the ID extension. - /// - Id = 1L << 11, - - /// - /// The server supports the CHILDREN extension. - /// - Children = 1L << 12, - - /// - /// The server supports the LOGINDISABLED extension. - /// - LoginDisabled = 1L << 13, - - /// - /// The server supports the STARTTLS extension. - /// - StartTLS = 1L << 14, - - /// - /// The server supports the MULTIAPPEND extension. - /// - MultiAppend = 1L << 15, - - /// - /// The server supports the BINARY content extension. - /// - Binary = 1L << 16, - - /// - /// The server supports the UNSELECT extension. - /// - Unselect = 1L << 17, - - /// - /// The server supports the UIDPLUS extension. - /// - UidPlus = 1L << 18, - - /// - /// The server supports the CATENATE extension. - /// - Catenate = 1L << 19, - - /// - /// The server supports the CONDSTORE extension. - /// - CondStore = 1L << 20, - - /// - /// The server supports the ESEARCH extension. - /// - ESearch = 1L << 21, - - /// - /// The server supports the SASL-IR extension. - /// - SaslIR = 1L << 22, - - /// - /// The server supports the COMPRESS extension. - /// - Compress = 1L << 23, - - /// - /// The server supports the WITHIN extension. - /// - Within = 1L << 24, - - /// - /// The server supports the ENABLE extension. - /// - Enable = 1L << 25, - - /// - /// The server supports the QRESYNC extension. - /// - QuickResync = 1L << 26, - - /// - /// The server supports the SEARCHRES extension. - /// - SearchResults = 1L << 27, - - /// - /// The server supports the SORT extension. - /// - Sort = 1L << 28, - - /// - /// The server supports the THREAD extension. - /// - Thread = 1L << 29, - - /// - /// The server supports the ANNOTATE extension. - /// - Annotate = 1L << 30, - - /// - /// The server supports the LIST-EXTENDED extension. - /// - ListExtended = 1L << 31, - - /// - /// The server supports the CONVERT extension. - /// - Convert = 1L << 32, - - /// - /// The server supports the LANGUAGE extension. - /// - Language = 1L << 33, - - /// - /// The server supports the I18NLEVEL extension. - /// - I18NLevel = 1L << 34, - - /// - /// The server supports the ESORT extension. - /// - ESort = 1L << 35, - - /// - /// The server supports the CONTEXT extension. - /// - Context = 1L << 36, - - /// - /// The server supports the METADATA extension. - /// - Metadata = 1L << 37, - - /// - /// The server supports the METADATA-SERVER extension. - /// - MetadataServer = 1L << 38, - - /// - /// The server supports the NOTIFY extension. - /// - Notify = 1L << 39, - - /// - /// The server supports the FILTERS extension. - /// - Filters = 1L << 40, - - /// - /// The server supports the LIST-STATUS extension. - /// - ListStatus = 1L << 41, - - /// - /// The server supports the SORT=DISPLAY extension. - /// - SortDisplay = 1L << 42, - - /// - /// The server supports the CREATE-SPECIAL-USE extension. - /// - CreateSpecialUse = 1L << 43, - - /// - /// The server supports the SPECIAL-USE extension. - /// - SpecialUse = 1L << 44, - - /// - /// The server supports the SEARCH=FUZZY extension. - /// - FuzzySearch = 1L << 45, - - /// - /// The server supports the MULTISEARCH extension. - /// - MultiSearch = 1L << 46, - - /// - /// The server supports the MOVE extension. - /// - Move = 1L << 47, - - /// - /// The server supports the UTF8=ACCEPT extension. - /// - UTF8Accept = 1L << 48, - - /// - /// The server supports the UTF8=ONLY extension. - /// - UTF8Only = 1L << 49, - - /// - /// The server supports the LITERAL- extension. - /// - LiteralMinus = 1L << 50, - - /// - /// The server supports the APPENDLIMIT extension. - /// - AppendLimit = 1L << 51, - - /// - /// The server supports the UNAUTHENTICATE extension. - /// - Unauthenticate = 1L << 52, - - /// - /// The server supports the STATUS=SIZE extension. - /// - StatusSize = 1L << 53, - - /// - /// The server supports the LIST-MYRIGHTS extension. - /// - ListMyRights = 1L << 54, - - /// - /// The server supports the OBJECTID extension. - /// - ObjectID = 1L << 55, - - /// - /// The server supports the REPLACE extension. - /// - Replace = 1L << 56, - - #region GMail Extensions - - /// - /// The server supports the XLIST extension (GMail). - /// - XList = 1L << 60, - - /// - /// The server supports the X-GM-EXT1 extension (GMail). - /// - GMailExt1 = 1L << 61 - - #endregion - } -} diff --git a/src/MailKit/Net/Imap/ImapClient.cs b/src/MailKit/Net/Imap/ImapClient.cs deleted file mode 100644 index acd199c..0000000 --- a/src/MailKit/Net/Imap/ImapClient.cs +++ /dev/null @@ -1,2616 +0,0 @@ -// -// ImapClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Net.Security; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; - -using MailKit.Security; - -using SslStream = MailKit.Net.SslStream; -using NetworkStream = MailKit.Net.NetworkStream; - -namespace MailKit.Net.Imap { - /// - /// An IMAP client that can be used to retrieve messages from a server. - /// - /// - /// The class supports both the "imap" and "imaps" - /// protocols. The "imap" protocol makes a clear-text connection to the IMAP - /// server and does not use SSL or TLS unless the IMAP server supports the - /// STARTTLS extension. - /// The "imaps" protocol, however, connects to the IMAP server using an - /// SSL-wrapped connection. - /// - /// - /// - /// - /// - /// - /// - public partial class ImapClient : MailStore, IImapClient - { - static readonly char[] ReservedUriCharacters = { ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '%' }; - const string HexAlphabet = "0123456789ABCDEF"; - - readonly ImapEngine engine; - int timeout = 2 * 60 * 1000; - string identifier; - bool disconnecting; - bool connecting; - bool disposed; - bool secure; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Before you can retrieve messages with the , you must first - /// call one of the Connect - /// methods and then authenticate with the one of the - /// Authenticate - /// methods. - /// - /// - /// - /// - public ImapClient () : this (new NullProtocolLogger ()) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Before you can retrieve messages with the , you must first - /// call one of the Connect - /// methods and then authenticate with the one of the - /// Authenticate - /// methods. - /// - /// - /// - /// - /// The protocol logger. - /// - /// is null. - /// - public ImapClient (IProtocolLogger protocolLogger) : base (protocolLogger) - { - // FIXME: should this take a ParserOptions argument? - engine = new ImapEngine (CreateImapFolder); - engine.MetadataChanged += OnEngineMetadataChanged; - engine.FolderCreated += OnEngineFolderCreated; - engine.Disconnected += OnEngineDisconnected; - engine.Alert += OnEngineAlert; - } - - /// - /// Gets an object that can be used to synchronize access to the IMAP server. - /// - /// - /// Gets an object that can be used to synchronize access to the IMAP server. - /// When using the non-Async methods from multiple threads, it is important to lock the - /// object for thread safety when using the synchronous methods. - /// - /// The lock object. - public override object SyncRoot { - get { return engine; } - } - - /// - /// Get the protocol supported by the message service. - /// - /// - /// Gets the protocol supported by the message service. - /// - /// The protocol. - protected override string Protocol { - get { return "imap"; } - } - - /// - /// Get the capabilities supported by the IMAP server. - /// - /// - /// The capabilities will not be known until a successful connection has been made via one of - /// the Connect methods and may - /// change as a side-effect of calling one of the - /// Authenticate - /// methods. - /// - /// - /// - /// - /// The capabilities. - /// - /// Capabilities cannot be enabled, they may only be disabled. - /// - public ImapCapabilities Capabilities { - get { return engine.Capabilities; } - set { - if ((engine.Capabilities | value) > engine.Capabilities) - throw new ArgumentException ("Capabilities cannot be enabled, they may only be disabled.", nameof (value)); - - engine.Capabilities = value; - } - } - - /// - /// Gets the maximum size of a message that can be appended to a folder. - /// - /// - /// Gets the maximum size of a message, in bytes, that can be appended to a folder. - /// If the value is not set, then the limit is unspecified. - /// - /// The append limit. - public uint? AppendLimit { - get { return engine.AppendLimit; } - } - - /// - /// Gets the internationalization level supported by the IMAP server. - /// - /// - /// Gets the internationalization level supported by the IMAP server. - /// For more information, see - /// section 4 of rfc5255. - /// - /// The internationalization level. - public int InternationalizationLevel { - get { return engine.I18NLevel; } - } - - /// - /// Get the access rights supported by the IMAP server. - /// - /// - /// These rights are additional rights supported by the IMAP server beyond the standard rights - /// defined in section 2.1 of rfc4314 - /// and will not be populated until the client is successfully connected. - /// - /// - /// - /// - /// The rights. - public AccessRights Rights { - get { return engine.Rights; } - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (ImapClient)); - } - - void CheckConnected () - { - if (!IsConnected) - throw new ServiceNotConnectedException ("The ImapClient is not connected."); - } - - void CheckAuthenticated () - { - if (!IsAuthenticated) - throw new ServiceNotAuthenticatedException ("The ImapClient is not authenticated."); - } - - /// - /// Instantiate a new . - /// - /// - /// Creates a new instance. - /// This method's purpose is to allow subclassing . - /// - /// The IMAP folder instance. - /// The constructior arguments. - /// - /// is null. - /// - protected virtual ImapFolder CreateImapFolder (ImapFolderConstructorArgs args) - { - var folder = new ImapFolder (args); - - folder.UpdateAppendLimit (AppendLimit); - - return folder; - } - - bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { - if (ServerCertificateValidationCallback != null) - return ServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (ServicePointManager.ServerCertificateValidationCallback != null) - return ServicePointManager.ServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); -#endif - - return DefaultServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); - } - - async Task CompressAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - - if ((engine.Capabilities & ImapCapabilities.Compress) == 0) - throw new NotSupportedException ("The IMAP server does not support the COMPRESS extension."); - - if (engine.State >= ImapEngineState.Selected) - throw new InvalidOperationException ("Compression must be enabled before selecting a folder."); - - int capabilitiesVersion = engine.CapabilitiesVersion; - var ic = engine.QueueCommand (cancellationToken, null, "COMPRESS DEFLATE\r\n"); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) { - for (int i = 0; i < ic.RespCodes.Count; i++) { - if (ic.RespCodes[i].Type == ImapResponseCodeType.CompressionActive) - return; - } - - throw ImapCommandException.Create ("COMPRESS", ic); - } - - engine.Stream.Stream = new CompressedStream (engine.Stream.Stream); - } - - /// - /// Enable compression over the IMAP connection. - /// - /// - /// Enables compression over the IMAP connection. - /// If the IMAP server supports the extension, - /// it is possible at any point after connecting to enable compression to reduce network - /// bandwidth usage. Ideally, this method should be called before authenticating. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Compression must be enabled before a folder has been selected. - /// - /// - /// The IMAP server does not support the extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the COMPRESS command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - public void Compress (CancellationToken cancellationToken = default (CancellationToken)) - { - CompressAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task EnableQuickResyncAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (engine.State != ImapEngineState.Authenticated) - throw new InvalidOperationException ("QRESYNC needs to be enabled immediately after authenticating."); - - if ((engine.Capabilities & ImapCapabilities.QuickResync) == 0) - throw new NotSupportedException ("The IMAP server does not support the QRESYNC extension."); - - if (engine.QResyncEnabled) - return; - - var ic = engine.QueueCommand (cancellationToken, null, "ENABLE QRESYNC CONDSTORE\r\n"); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("ENABLE", ic); - } - - /// - /// Enable the QRESYNC feature. - /// - /// - /// Enables the QRESYNC feature. - /// The QRESYNC extension improves resynchronization performance of folders by - /// querying the IMAP server for a list of changes when the folder is opened using the - /// - /// method. - /// If this feature is enabled, the event is replaced - /// with the event. - /// This method needs to be called immediately after calling one of the - /// Authenticate methods, before - /// opening any folders. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Quick resynchronization needs to be enabled before selecting a folder. - /// - /// - /// The IMAP server does not support the QRESYNC extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ENABLE command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - public override void EnableQuickResync (CancellationToken cancellationToken = default (CancellationToken)) - { - EnableQuickResyncAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task EnableUTF8Async (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (engine.State != ImapEngineState.Authenticated) - throw new InvalidOperationException ("UTF8=ACCEPT needs to be enabled immediately after authenticating."); - - if ((engine.Capabilities & ImapCapabilities.UTF8Accept) == 0) - throw new NotSupportedException ("The IMAP server does not support the UTF8=ACCEPT extension."); - - if (engine.UTF8Enabled) - return; - - var ic = engine.QueueCommand (cancellationToken, null, "ENABLE UTF8=ACCEPT\r\n"); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("ENABLE", ic); - } - - /// - /// Enable the UTF8=ACCEPT extension. - /// - /// - /// Enables the UTF8=ACCEPT extension. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// UTF8=ACCEPT needs to be enabled before selecting a folder. - /// - /// - /// The IMAP server does not support the UTF8=ACCEPT extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ENABLE command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - public void EnableUTF8 (CancellationToken cancellationToken = default (CancellationToken)) - { - EnableUTF8Async (false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task IdentifyAsync (ImapImplementation clientImplementation, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - - if ((engine.Capabilities & ImapCapabilities.Id) == 0) - throw new NotSupportedException ("The IMAP server does not support the ID extension."); - - var command = new StringBuilder ("ID "); - var args = new List (); - - if (clientImplementation != null && clientImplementation.Properties.Count > 0) { - command.Append ('('); - foreach (var property in clientImplementation.Properties) { - command.Append ("%Q "); - args.Add (property.Key); - - if (property.Value != null) { - command.Append ("%Q "); - args.Add (property.Value); - } else { - command.Append ("NIL "); - } - } - command[command.Length - 1] = ')'; - command.Append ("\r\n"); - } else { - command.Append ("NIL\r\n"); - } - - var ic = new ImapCommand (engine, cancellationToken, null, command.ToString (), args.ToArray ()); - ic.RegisterUntaggedHandler ("ID", ImapUtils.ParseImplementationAsync); - - engine.QueueCommand (ic); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("ID", ic); - - return (ImapImplementation) ic.UserData; - } - - /// - /// Identify the client implementation to the server and obtain the server implementation details. - /// - /// - /// Passes along the client implementation details to the server while also obtaining implementation - /// details from the server. - /// If the is null or no properties have been set, no - /// identifying information will be sent to the server. - /// - /// Security Implications - /// This command has the danger of violating the privacy of users if misused. Clients should - /// notify users that they send the ID command. - /// It is highly desirable that implementations provide a method of disabling ID support, perhaps by - /// not calling this method at all, or by passing null as the - /// argument. - /// Implementors must exercise extreme care in adding properties to the . - /// Some properties, such as a processor ID number, Ethernet address, or other unique (or mostly unique) identifier - /// would allow tracking of users in ways that violate user privacy expectations and may also make it easier for - /// attackers to exploit security holes in the client. - /// - /// - /// - /// - /// - /// The implementation details of the server if available; otherwise, null. - /// The client implementation. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The IMAP server does not support the ID extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the ID command with a NO or BAD response. - /// - /// - /// An IMAP protocol error occurred. - /// - public ImapImplementation Identify (ImapImplementation clientImplementation, CancellationToken cancellationToken = default (CancellationToken)) - { - return IdentifyAsync (clientImplementation, false, cancellationToken).GetAwaiter ().GetResult (); - } - - #region IMailService implementation - - /// - /// Get the authentication mechanisms supported by the IMAP server. - /// - /// - /// The authentication mechanisms are queried as part of the - /// Connect - /// method. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before authenticating. - /// - /// - /// - /// - /// The authentication mechanisms. - public override HashSet AuthenticationMechanisms { - get { return engine.AuthenticationMechanisms; } - } - - /// - /// Get the threading algorithms supported by the IMAP server. - /// - /// - /// The threading algorithms are queried as part of the - /// Connect - /// and Authenticate methods. - /// - /// - /// - /// - /// The supported threading algorithms. - public override HashSet ThreadingAlgorithms { - get { return engine.ThreadingAlgorithms; } - } - - /// - /// Get or set the timeout for network streaming operations, in milliseconds. - /// - /// - /// Gets or sets the underlying socket stream's - /// and values. - /// - /// The timeout in milliseconds. - public override int Timeout { - get { return timeout; } - set { - if (IsConnected && engine.Stream.CanTimeout) { - engine.Stream.WriteTimeout = value; - engine.Stream.ReadTimeout = value; - } - - timeout = value; - } - } - - /// - /// Get whether or not the client is currently connected to an IMAP server. - /// - /// - /// The state is set to true immediately after - /// one of the Connect - /// methods succeeds and is not set back to false until either the client - /// is disconnected via or until an - /// is thrown while attempting to read or write to - /// the underlying network socket. - /// When an is caught, the connection state of the - /// should be checked before continuing. - /// - /// true if the client is connected; otherwise, false. - public override bool IsConnected { - get { return engine.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). - /// - /// true if the connection is secure; otherwise, false. - public override bool IsSecure { - get { return IsConnected && secure; } - } - - /// - /// Get whether or not the client is currently authenticated with the IMAP server. - /// - /// - /// Gets whether or not the client is currently authenticated with the IMAP server. - /// To authenticate with the IMAP server, use one of the - /// Authenticate - /// methods. - /// - /// true if the client is connected; otherwise, false. - public override bool IsAuthenticated { - get { return engine.State >= ImapEngineState.Authenticated; } - } - - /// - /// Get whether or not the client is currently in the IDLE state. - /// - /// - /// Gets whether or not the client is currently in the IDLE state. - /// - /// true if an IDLE command is active; otherwise, false. - public bool IsIdle { - get { return engine.State == ImapEngineState.Idle; } - } - - static AuthenticationException CreateAuthenticationException (ImapCommand ic) - { - for (int i = 0; i < ic.RespCodes.Count; i++) { - if (ic.RespCodes[i].IsError || ic.RespCodes[i].Type == ImapResponseCodeType.Alert) - return new AuthenticationException (ic.RespCodes[i].Message); - } - - if (ic.ResponseText != null) - return new AuthenticationException (ic.ResponseText); - - return new AuthenticationException (); - } - - void EmitAndThrowOnAlert (ImapCommand ic) - { - for (int i = 0; i < ic.RespCodes.Count; i++) { - if (ic.RespCodes[i].Type != ImapResponseCodeType.Alert) - continue; - - OnAlert (ic.RespCodes[i].Message); - - throw new AuthenticationException (ic.ResponseText ?? ic.RespCodes[i].Message); - } - } - - static bool IsHexDigit (char c) - { - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); - } - - static uint HexUnescape (uint c) - { - if (c >= 'a') - return (c - 'a') + 10; - - if (c >= 'A') - return (c - 'A') + 10; - - return c - '0'; - } - - static char HexUnescape (string pattern, ref int index) - { - uint value, c; - - if (pattern[index++] != '%' || !IsHexDigit (pattern[index]) || !IsHexDigit (pattern[index + 1])) - return '%'; - - c = (uint) pattern[index++]; - value = HexUnescape (c) << 4; - c = pattern[index++]; - value |= HexUnescape (c); - - return (char) value; - } - - internal static string UnescapeUserName (string escaped) - { - StringBuilder userName; - int startIndex, index; - - if ((index = escaped.IndexOf ('%')) == -1) - return escaped; - - userName = new StringBuilder (); - startIndex = 0; - - do { - userName.Append (escaped, startIndex, index - startIndex); - userName.Append (HexUnescape (escaped, ref index)); - startIndex = index; - - if (startIndex >= escaped.Length) - break; - - index = escaped.IndexOf ('%', startIndex); - } while (index != -1); - - if (index == -1) - userName.Append (escaped, startIndex, escaped.Length - startIndex); - - return userName.ToString (); - } - - static string HexEscape (char c) - { - return "%" + HexAlphabet[(c >> 4) & 0xF] + HexAlphabet[c & 0xF]; - } - - internal static string EscapeUserName (string userName) - { - StringBuilder escaped; - int startIndex, index; - - if ((index = userName.IndexOfAny (ReservedUriCharacters)) == -1) - return userName; - - escaped = new StringBuilder (); - startIndex = 0; - - do { - escaped.Append (userName, startIndex, index - startIndex); - escaped.Append (HexEscape (userName[index++])); - startIndex = index; - - if (startIndex >= userName.Length) - break; - - index = userName.IndexOfAny (ReservedUriCharacters, startIndex); - } while (index != -1); - - if (index == -1) - escaped.Append (userName, startIndex, userName.Length - startIndex); - - return escaped.ToString (); - } - - string GetSessionIdentifier (string userName) - { - var uri = engine.Uri; - - return string.Format (CultureInfo.InvariantCulture, "{0}://{1}@{2}:{3}", uri.Scheme, EscapeUserName (userName), uri.Host, uri.Port); - } - - async Task OnAuthenticatedAsync (string message, bool doAsync, CancellationToken cancellationToken) - { - await engine.QueryNamespacesAsync (doAsync, cancellationToken).ConfigureAwait (false); - await engine.QuerySpecialFoldersAsync (doAsync, cancellationToken).ConfigureAwait (false); - OnAuthenticated (message); - } - - async Task AuthenticateAsync (SaslMechanism mechanism, bool doAsync, CancellationToken cancellationToken) - { - if (mechanism == null) - throw new ArgumentNullException (nameof (mechanism)); - - CheckDisposed (); - CheckConnected (); - - if (engine.State >= ImapEngineState.Authenticated) - throw new InvalidOperationException ("The ImapClient is already authenticated."); - - int capabilitiesVersion = engine.CapabilitiesVersion; - var uri = new Uri ("imap://" + engine.Uri.Host); - NetworkCredential cred; - ImapCommand ic = null; - string id; - - cancellationToken.ThrowIfCancellationRequested (); - - mechanism.Uri = uri; - - var command = string.Format ("AUTHENTICATE {0}", mechanism.MechanismName); - - if ((engine.Capabilities & ImapCapabilities.SaslIR) != 0 && mechanism.SupportsInitialResponse) { - var ir = mechanism.Challenge (null); - command += " " + ir + "\r\n"; - } else { - command += "\r\n"; - } - - ic = engine.QueueCommand (cancellationToken, null, command); - ic.ContinuationHandler = async (imap, cmd, text, xdoAsync) => { - string challenge; - - if (mechanism.IsAuthenticated) { - // The server claims we aren't done authenticating, but our SASL mechanism thinks we are... - // Send an empty string to abort the AUTHENTICATE command. - challenge = string.Empty; - } else { - challenge = mechanism.Challenge (text); - } - - var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - - if (xdoAsync) { - await imap.Stream.WriteAsync (buf, 0, buf.Length, cmd.CancellationToken).ConfigureAwait (false); - await imap.Stream.FlushAsync (cmd.CancellationToken).ConfigureAwait (false); - } else { - imap.Stream.Write (buf, 0, buf.Length, cmd.CancellationToken); - imap.Stream.Flush (cmd.CancellationToken); - } - }; - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) { - EmitAndThrowOnAlert (ic); - - throw new AuthenticationException (); - } - - engine.State = ImapEngineState.Authenticated; - - cred = mechanism.Credentials.GetCredential (mechanism.Uri, mechanism.MechanismName); - id = GetSessionIdentifier (cred.UserName); - if (id != identifier) { - engine.FolderCache.Clear (); - identifier = id; - } - - // Query the CAPABILITIES again if the server did not include an - // untagged CAPABILITIES response to the AUTHENTICATE command. - if (engine.CapabilitiesVersion == capabilitiesVersion) - await engine.QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - - await OnAuthenticatedAsync (ic.ResponseText ?? string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override void Authenticate (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)) - { - AuthenticateAsync (mechanism, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task AuthenticateAsync (Encoding encoding, ICredentials credentials, bool doAsync, CancellationToken cancellationToken) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (credentials == null) - throw new ArgumentNullException (nameof (credentials)); - - CheckDisposed (); - CheckConnected (); - - if (engine.State >= ImapEngineState.Authenticated) - throw new InvalidOperationException ("The ImapClient is already authenticated."); - - int capabilitiesVersion = engine.CapabilitiesVersion; - var uri = new Uri ("imap://" + engine.Uri.Host); - NetworkCredential cred; - ImapCommand ic = null; - SaslMechanism sasl; - string id; - - foreach (var authmech in SaslMechanism.AuthMechanismRank) { - if (!engine.AuthenticationMechanisms.Contains (authmech)) - continue; - - if ((sasl = SaslMechanism.Create (authmech, uri, encoding, credentials)) == null) - continue; - - cancellationToken.ThrowIfCancellationRequested (); - - var command = string.Format ("AUTHENTICATE {0}", sasl.MechanismName); - - if ((engine.Capabilities & ImapCapabilities.SaslIR) != 0 && sasl.SupportsInitialResponse) { - var ir = sasl.Challenge (null); - command += " " + ir + "\r\n"; - } else { - command += "\r\n"; - } - - ic = engine.QueueCommand (cancellationToken, null, command); - ic.ContinuationHandler = async (imap, cmd, text, xdoAsync) => { - string challenge; - - if (sasl.IsAuthenticated) { - // The server claims we aren't done authenticating, but our SASL mechanism thinks we are... - // Send an empty string to abort the AUTHENTICATE command. - challenge = string.Empty; - } else { - challenge = sasl.Challenge (text); - } - - var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - - if (xdoAsync) { - await imap.Stream.WriteAsync (buf, 0, buf.Length, cmd.CancellationToken).ConfigureAwait (false); - await imap.Stream.FlushAsync (cmd.CancellationToken).ConfigureAwait (false); - } else { - imap.Stream.Write (buf, 0, buf.Length, cmd.CancellationToken); - imap.Stream.Flush (cmd.CancellationToken); - } - }; - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) { - EmitAndThrowOnAlert (ic); - if (ic.Bye) - throw new ImapProtocolException (ic.ResponseText); - continue; - } - - engine.State = ImapEngineState.Authenticated; - - cred = credentials.GetCredential (uri, sasl.MechanismName); - id = GetSessionIdentifier (cred.UserName); - if (id != identifier) { - engine.FolderCache.Clear (); - identifier = id; - } - - // Query the CAPABILITIES again if the server did not include an - // untagged CAPABILITIES response to the AUTHENTICATE command. - if (engine.CapabilitiesVersion == capabilitiesVersion) - await engine.QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - - await OnAuthenticatedAsync (ic.ResponseText ?? string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - return; - } - - if ((Capabilities & ImapCapabilities.LoginDisabled) != 0) { - if (ic == null) - throw new AuthenticationException ("The LOGIN command is disabled."); - - throw CreateAuthenticationException (ic); - } - - // fall back to the classic LOGIN command... - cred = credentials.GetCredential (uri, "DEFAULT"); - - ic = engine.QueueCommand (cancellationToken, null, "LOGIN %S %S\r\n", cred.UserName, cred.Password); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw CreateAuthenticationException (ic); - - engine.State = ImapEngineState.Authenticated; - - id = GetSessionIdentifier (cred.UserName); - if (id != identifier) { - engine.FolderCache.Clear (); - identifier = id; - } - - // Query the CAPABILITIES again if the server did not include an - // untagged CAPABILITIES response to the LOGIN command. - if (engine.CapabilitiesVersion == capabilitiesVersion) - await engine.QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - - await OnAuthenticatedAsync (ic.ResponseText ?? string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Authenticate using the supplied credentials. - /// - /// - /// If the IMAP server supports one or more SASL authentication mechanisms, - /// then the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then LOGIN command is used as a fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// The text encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override void Authenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)) - { - AuthenticateAsync (encoding, credentials, false, cancellationToken).GetAwaiter ().GetResult (); - } - - internal void ReplayConnect (string host, Stream replayStream, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (replayStream == null) - throw new ArgumentNullException (nameof (replayStream)); - - engine.Uri = new Uri ($"imap://{host}:143"); - engine.ConnectAsync (new ImapStream (replayStream, ProtocolLogger), false, cancellationToken).GetAwaiter ().GetResult (); - engine.TagPrefix = 'A'; - secure = false; - - if (engine.CapabilitiesVersion == 0) - engine.QueryCapabilitiesAsync (false, cancellationToken).GetAwaiter ().GetResult (); - - // Note: we capture the state here in case someone calls Authenticate() from within the Connected event handler. - var authenticated = engine.State == ImapEngineState.Authenticated; - - OnConnected (host, 143, SecureSocketOptions.None); - - if (authenticated) - OnAuthenticatedAsync (string.Empty, false, cancellationToken).GetAwaiter ().GetResult (); - } - - internal async Task ReplayConnectAsync (string host, Stream replayStream, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (replayStream == null) - throw new ArgumentNullException (nameof (replayStream)); - - engine.Uri = new Uri ($"imap://{host}:143"); - await engine.ConnectAsync (new ImapStream (replayStream, ProtocolLogger), true, cancellationToken).ConfigureAwait (false); - engine.TagPrefix = 'A'; - secure = false; - - if (engine.CapabilitiesVersion == 0) - await engine.QueryCapabilitiesAsync (true, cancellationToken).ConfigureAwait (false); - - // Note: we capture the state here in case someone calls Authenticate() from within the Connected event handler. - var authenticated = engine.State == ImapEngineState.Authenticated; - - OnConnected (host, 143, SecureSocketOptions.None); - - if (authenticated) - await OnAuthenticatedAsync (string.Empty, true, cancellationToken).ConfigureAwait (false); - } - - internal static void ComputeDefaultValues (string host, ref int port, ref SecureSocketOptions options, out Uri uri, out bool starttls) - { - switch (options) { - default: - if (port == 0) - port = 143; - break; - case SecureSocketOptions.Auto: - switch (port) { - case 0: port = 143; goto default; - case 993: options = SecureSocketOptions.SslOnConnect; break; - default: options = SecureSocketOptions.StartTlsWhenAvailable; break; - } - break; - case SecureSocketOptions.SslOnConnect: - if (port == 0) - port = 993; - break; - } - - switch (options) { - case SecureSocketOptions.StartTlsWhenAvailable: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "imap://{0}:{1}/?starttls=when-available", host, port)); - starttls = true; - break; - case SecureSocketOptions.StartTls: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "imap://{0}:{1}/?starttls=always", host, port)); - starttls = true; - break; - case SecureSocketOptions.SslOnConnect: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "imaps://{0}:{1}", host, port)); - starttls = false; - break; - default: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "imap://{0}:{1}", host, port)); - starttls = false; - break; - } - } - - async Task ConnectAsync (string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0) - throw new ArgumentException ("The host name cannot be empty.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - CheckDisposed (); - - if (IsConnected) - throw new InvalidOperationException ("The ImapClient is already connected."); - - Stream stream; - bool starttls; - Uri uri; - - ComputeDefaultValues (host, ref port, ref options, out uri, out starttls); - - var socket = await ConnectSocket (host, port, doAsync, cancellationToken).ConfigureAwait (false); - - engine.Uri = uri; - - if (options == SecureSocketOptions.SslOnConnect) { - var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); - - try { - if (doAsync) { - await ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - ssl.Dispose (); - - throw SslHandshakeException.Create (this, ex, false); - } - - secure = true; - stream = ssl; - } else { - stream = new NetworkStream (socket, true); - secure = false; - } - - if (stream.CanTimeout) { - stream.WriteTimeout = timeout; - stream.ReadTimeout = timeout; - } - - try { - ProtocolLogger.LogConnect (uri); - } catch { - stream.Dispose (); - secure = false; - throw; - } - - connecting = true; - - try { - await engine.ConnectAsync (new ImapStream (stream, ProtocolLogger), doAsync, cancellationToken).ConfigureAwait (false); - } catch { - connecting = false; - secure = false; - throw; - } - - try { - // Only query the CAPABILITIES if the greeting didn't include them. - if (engine.CapabilitiesVersion == 0) - await engine.QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (options == SecureSocketOptions.StartTls && (engine.Capabilities & ImapCapabilities.StartTLS) == 0) - throw new NotSupportedException ("The IMAP server does not support the STARTTLS extension."); - - if (starttls && (engine.Capabilities & ImapCapabilities.StartTLS) != 0) { - var ic = engine.QueueCommand (cancellationToken, null, "STARTTLS\r\n"); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response == ImapCommandResponse.Ok) { - try { - var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream.Stream = tls; - - if (doAsync) { - await tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - throw SslHandshakeException.Create (this, ex, true); - } - - secure = true; - - // Query the CAPABILITIES again if the server did not include an - // untagged CAPABILITIES response to the STARTTLS command. - if (engine.CapabilitiesVersion == 1) - await engine.QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - } else if (options == SecureSocketOptions.StartTls) { - throw ImapCommandException.Create ("STARTTLS", ic); - } - } - } catch { - secure = false; - engine.Disconnect (); - throw; - } finally { - connecting = false; - } - - // Note: we capture the state here in case someone calls Authenticate() from within the Connected event handler. - var authenticated = engine.State == ImapEngineState.Authenticated; - - OnConnected (host, port, options); - - if (authenticated) - await OnAuthenticatedAsync (string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Establish a connection to the specified IMAP server. - /// - /// - /// Establishes a connection to the specified IMAP or IMAP/S server. - /// If the has a value of 0, then the - /// parameter is used to determine the default port to - /// connect to. The default port used with - /// is 993. All other values will use a default port of 143. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 993, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// - /// - /// - /// - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the IMAP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task ConnectAsync (Stream stream, string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0) - throw new ArgumentException ("The host name cannot be empty.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - CheckDisposed (); - - if (IsConnected) - throw new InvalidOperationException ("The ImapClient is already connected."); - - Stream network; - bool starttls; - Uri uri; - - ComputeDefaultValues (host, ref port, ref options, out uri, out starttls); - - engine.Uri = uri; - - if (options == SecureSocketOptions.SslOnConnect) { - var ssl = new SslStream (stream, false, ValidateRemoteCertificate); - - try { - if (doAsync) { - await ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - ssl.Dispose (); - - throw SslHandshakeException.Create (this, ex, false); - } - - network = ssl; - secure = true; - } else { - network = stream; - secure = false; - } - - if (network.CanTimeout) { - network.WriteTimeout = timeout; - network.ReadTimeout = timeout; - } - - try { - ProtocolLogger.LogConnect (uri); - } catch { - network.Dispose (); - secure = false; - throw; - } - - connecting = true; - - try { - await engine.ConnectAsync (new ImapStream (network, ProtocolLogger), doAsync, cancellationToken).ConfigureAwait (false); - } catch { - connecting = false; - throw; - } - - try { - // Only query the CAPABILITIES if the greeting didn't include them. - if (engine.CapabilitiesVersion == 0) - await engine.QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (options == SecureSocketOptions.StartTls && (engine.Capabilities & ImapCapabilities.StartTLS) == 0) - throw new NotSupportedException ("The IMAP server does not support the STARTTLS extension."); - - if (starttls && (engine.Capabilities & ImapCapabilities.StartTLS) != 0) { - var ic = engine.QueueCommand (cancellationToken, null, "STARTTLS\r\n"); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response == ImapCommandResponse.Ok) { - var tls = new SslStream (network, false, ValidateRemoteCertificate); - engine.Stream.Stream = tls; - - try { - if (doAsync) { - await tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - throw SslHandshakeException.Create (this, ex, true); - } - - secure = true; - - // Query the CAPABILITIES again if the server did not include an - // untagged CAPABILITIES response to the STARTTLS command. - if (engine.CapabilitiesVersion == 1) - await engine.QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - } else if (options == SecureSocketOptions.StartTls) { - throw ImapCommandException.Create ("STARTTLS", ic); - } - } - } catch { - secure = false; - engine.Disconnect (); - throw; - } finally { - connecting = false; - } - - // Note: we capture the state here in case someone calls Authenticate() from within the Connected event handler. - var authenticated = engine.State == ImapEngineState.Authenticated; - - OnConnected (host, port, options); - - if (authenticated) - await OnAuthenticatedAsync (string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - } - - Task ConnectAsync (Socket socket, string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (socket == null) - throw new ArgumentNullException (nameof (socket)); - - if (!socket.Connected) - throw new ArgumentException ("The socket is not connected.", nameof (socket)); - - return ConnectAsync (new NetworkStream (socket, true), host, port, options, doAsync, cancellationToken); - } - - /// - /// Establish a connection to the specified IMAP or IMAP/S server using the provided socket. - /// - /// - /// Establishes a connection to the specified IMAP or IMAP/S server using - /// the provided socket. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 993, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the IMAP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override void Connect (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (socket, host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Establish a connection to the specified IMAP or IMAP/S server using the provided stream. - /// - /// - /// Establishes a connection to the specified IMAP or IMAP/S server using - /// the provided stream. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 993, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the IMAP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP command failed. - /// - /// - /// An IMAP protocol error occurred. - /// - public override void Connect (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (stream, host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task DisconnectAsync (bool quit, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (!engine.IsConnected) - return; - - if (quit) { - try { - var ic = engine.QueueCommand (cancellationToken, null, "LOGOUT\r\n"); - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - } catch (OperationCanceledException) { - } catch (ImapProtocolException) { - } catch (ImapCommandException) { - } catch (IOException) { - } - } - - disconnecting = true; - - engine.Disconnect (); - } - - /// - /// Disconnect the service. - /// - /// - /// If is true, a LOGOUT command will be issued in order to disconnect cleanly. - /// - /// - /// - /// - /// If set to true, a LOGOUT command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - public override void Disconnect (bool quit, CancellationToken cancellationToken = default (CancellationToken)) - { - DisconnectAsync (quit, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task NoOpAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - var ic = engine.QueueCommand (cancellationToken, null, "NOOP\r\n"); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("NOOP", ic); - } - - /// - /// Ping the IMAP server to keep the connection alive. - /// - /// - /// The NOOP command is typically used to keep the connection with the IMAP server - /// alive. When a client goes too long (typically 30 minutes) without sending any commands to the - /// IMAP server, the IMAP server will close the connection with the client, forcing the client to - /// reconnect before it can send any more commands. - /// The NOOP command also provides a great way for a client to check for new - /// messages. - /// When the IMAP server receives a NOOP command, it will reply to the client with a - /// list of pending updates such as EXISTS and RECENT counts on the currently - /// selected folder. To receive these notifications, subscribe to the - /// and events, - /// respectively. - /// For more information about the NOOP command, see - /// rfc3501. - /// - /// - /// - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOOP command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public override void NoOp (CancellationToken cancellationToken = default (CancellationToken)) - { - NoOpAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task IdleAsync (CancellationToken doneToken, bool doAsync, CancellationToken cancellationToken) - { - if (!doneToken.CanBeCanceled) - throw new ArgumentException ("The doneToken must be cancellable.", nameof (doneToken)); - - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if ((engine.Capabilities & ImapCapabilities.Idle) == 0) - throw new NotSupportedException ("The IMAP server does not support the IDLE extension."); - - if (engine.State != ImapEngineState.Selected) - throw new InvalidOperationException ("An ImapFolder has not been opened."); - - if (doneToken.IsCancellationRequested) - return; - - using (var context = new ImapIdleContext (engine, doneToken, cancellationToken)) { - var ic = engine.QueueCommand (cancellationToken, null, "IDLE\r\n"); - ic.ContinuationHandler = context.ContinuationHandler; - ic.UserData = context; - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("IDLE", ic); - } - } - - /// - /// Toggle the into the IDLE state. - /// - /// - /// When a client enters the IDLE state, the IMAP server will send - /// events to the client as they occur on the selected folder. These events - /// may include notifications of new messages arriving, expunge notifications, - /// flag changes, etc. - /// Due to the nature of the IDLE command, a folder must be selected - /// before a client can enter into the IDLE state. This can be done by - /// opening a folder using - /// - /// or any of the other variants. - /// While the IDLE command is running, no other commands may be issued until the - /// is cancelled. - /// It is especially important to cancel the - /// before cancelling the when using SSL or TLS due to - /// the fact that cannot be polled. - /// - /// - /// - /// - /// The cancellation token used to return to the non-idle state. - /// The cancellation token. - /// - /// must be cancellable (i.e. cannot be used). - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// A has not been opened. - /// - /// - /// The IMAP server does not support the IDLE extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the IDLE command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public void Idle (CancellationToken doneToken, CancellationToken cancellationToken = default (CancellationToken)) - { - IdleAsync (doneToken, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task NotifyAsync (bool status, IList eventGroups, bool doAsync, CancellationToken cancellationToken) - { - if (eventGroups == null) - throw new ArgumentNullException (nameof (eventGroups)); - - if (eventGroups.Count == 0) - throw new ArgumentException ("No event groups specified.", nameof (eventGroups)); - - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if ((engine.Capabilities & ImapCapabilities.Notify) == 0) - throw new NotSupportedException ("The IMAP server does not support the NOTIFY extension."); - - var command = new StringBuilder ("NOTIFY SET"); - var notifySelectedNewExpunge = false; - var args = new List (); - - if (status) - command.Append (" STATUS"); - - foreach (var group in eventGroups) { - command.Append (" "); - - group.Format (engine, command, args, ref notifySelectedNewExpunge); - } - - command.Append ("\r\n"); - - var ic = new ImapCommand (engine, cancellationToken, null, command.ToString (), args.ToArray ()); - - engine.QueueCommand (ic); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("NOTIFY", ic); - - engine.NotifySelectedNewExpunge = notifySelectedNewExpunge; - } - - /// - /// Request the specified notification events from the IMAP server. - /// - /// - /// The NOTIFY command is used to expand - /// which notifications the client wishes to be notified about, including status notifications - /// about folders other than the currently selected folder. It can also be used to automatically - /// FETCH information about new messages that have arrived in the currently selected folder. - /// This, combined with , - /// can be used to get instant notifications for changes to any of the specified folders. - /// - /// true if the server should immediately notify the client of the - /// selected folder's status; otherwise, false. - /// The specific event groups that the client would like to receive notifications for. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// One or more is invalid. - /// - /// - /// The IMAP server does not support the NOTIFY extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOTIFY command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public void Notify (bool status, IList eventGroups, CancellationToken cancellationToken = default (CancellationToken)) - { - NotifyAsync (status, eventGroups, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task DisableNotifyAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if ((engine.Capabilities & ImapCapabilities.Notify) == 0) - throw new NotSupportedException ("The IMAP server does not support the NOTIFY extension."); - - var ic = new ImapCommand (engine, cancellationToken, null, "NOTIFY NONE\r\n"); - - engine.QueueCommand (ic); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("NOTIFY", ic); - - engine.NotifySelectedNewExpunge = false; - } - - /// - /// Disable any previously requested notification events from the IMAP server. - /// - /// - /// Disables any notification events requested in a prior call to - /// . - /// request. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the NOTIFY extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the NOTIFY command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public void DisableNotify (CancellationToken cancellationToken = default (CancellationToken)) - { - DisableNotifyAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - #endregion - - #region IMailStore implementation - - /// - /// Get the personal namespaces. - /// - /// - /// The personal folder namespaces contain a user's personal mailbox folders. - /// - /// The personal namespaces. - public override FolderNamespaceCollection PersonalNamespaces { - get { return engine.PersonalNamespaces; } - } - - /// - /// Get the shared namespaces. - /// - /// - /// The shared folder namespaces contain mailbox folders that are shared with the user. - /// - /// The shared namespaces. - public override FolderNamespaceCollection SharedNamespaces { - get { return engine.SharedNamespaces; } - } - - /// - /// Get the other namespaces. - /// - /// - /// The other folder namespaces contain other mailbox folders. - /// - /// The other namespaces. - public override FolderNamespaceCollection OtherNamespaces { - get { return engine.OtherNamespaces; } - } - - /// - /// Get whether or not the mail store supports quotas. - /// - /// - /// Gets whether or not the mail store supports quotas. - /// - /// true if the mail store supports quotas; otherwise, false. - public override bool SupportsQuotas { - get { return (engine.Capabilities & ImapCapabilities.Quota) != 0; } - } - - /// - /// Get the Inbox folder. - /// - /// - /// The Inbox folder is the default folder and always exists on the server. - /// This property will only be available after the client has been authenticated. - /// - /// - /// - /// - /// The Inbox folder. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - public override IMailFolder Inbox { - get { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - return engine.Inbox; - } - } - - /// - /// Get the specified special folder. - /// - /// - /// Not all IMAP servers support special folders. Only IMAP servers - /// supporting the or - /// extensions may have - /// special folders. - /// - /// The folder if available; otherwise null. - /// The type of special folder. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the SPECIAL-USE nor XLIST extensions. - /// - public override IMailFolder GetFolder (SpecialFolder folder) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if ((Capabilities & (ImapCapabilities.SpecialUse | ImapCapabilities.XList)) == 0) - throw new NotSupportedException ("The IMAP server does not support the SPECIAL-USE nor XLIST extensions."); - - switch (folder) { - case SpecialFolder.All: return engine.All; - case SpecialFolder.Archive: return engine.Archive; - case SpecialFolder.Drafts: return engine.Drafts; - case SpecialFolder.Flagged: return engine.Flagged; - case SpecialFolder.Important: return engine.Important; - case SpecialFolder.Junk: return engine.Junk; - case SpecialFolder.Sent: return engine.Sent; - case SpecialFolder.Trash: return engine.Trash; - default: throw new ArgumentOutOfRangeException (nameof (folder)); - } - } - - /// - /// Get the folder for the specified namespace. - /// - /// - /// Gets the folder for the specified namespace. - /// - /// The folder. - /// The namespace. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder could not be found. - /// - public override IMailFolder GetFolder (FolderNamespace @namespace) - { - if (@namespace == null) - throw new ArgumentNullException (nameof (@namespace)); - - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - var encodedName = engine.EncodeMailboxName (@namespace.Path); - ImapFolder folder; - - if (engine.GetCachedFolder (encodedName, out folder)) - return folder; - - throw new FolderNotFoundException (@namespace.Path); - } - - async Task> GetFoldersAsync (FolderNamespace @namespace, StatusItems items, bool subscribedOnly, bool doAsync, CancellationToken cancellationToken) - { - if (@namespace == null) - throw new ArgumentNullException (nameof (@namespace)); - - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - var folders = await engine.GetFoldersAsync (@namespace, items, subscribedOnly, doAsync, cancellationToken).ConfigureAwait (false); - var list = new IMailFolder[folders.Count]; - - for (int i = 0; i < list.Length; i++) - list[i] = (IMailFolder) folders[i]; - - return list; - } - - /// - /// Get all of the folders within the specified namespace. - /// - /// - /// Gets all of the folders within the specified namespace. - /// - /// The folders. - /// The namespace. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The namespace folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the LIST or LSUB command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public override IList GetFolders (FolderNamespace @namespace, StatusItems items = StatusItems.None, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetFoldersAsync (@namespace, items, subscribedOnly, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the folder for the specified path. - /// - /// - /// Gets the folder for the specified path. - /// - /// The folder. - /// The folder path. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The folder could not be found. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server replied to the IDLE command with a NO or BAD response. - /// - /// - /// The server responded with an unexpected token. - /// - public override IMailFolder GetFolder (string path, CancellationToken cancellationToken = default (CancellationToken)) - { - if (path == null) - throw new ArgumentNullException (nameof (path)); - - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - return engine.GetFolderAsync (path, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task GetMetadataAsync (MetadataTag tag, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if ((engine.Capabilities & (ImapCapabilities.Metadata | ImapCapabilities.MetadataServer)) == 0) - throw new NotSupportedException ("The IMAP server does not support the METADATA extension."); - - var ic = new ImapCommand (engine, cancellationToken, null, "GETMETADATA \"\" %S\r\n", tag.Id); - ic.RegisterUntaggedHandler ("METADATA", ImapUtils.ParseMetadataAsync); - var metadata = new MetadataCollection (); - ic.UserData = metadata; - - engine.QueueCommand (ic); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("GETMETADATA", ic); - - string value = null; - - for (int i = 0; i < metadata.Count; i++) { - if (metadata[i].EncodedName.Length == 0 && metadata[i].Tag.Id == tag.Id) { - value = metadata[i].Value; - metadata.RemoveAt (i); - break; - } - } - - engine.ProcessMetadataChanges (metadata); - - return value; - } - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA or METADATA-SERVER extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (tag, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task GetMetadataAsync (MetadataOptions options, IEnumerable tags, bool doAsync, CancellationToken cancellationToken) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (tags == null) - throw new ArgumentNullException (nameof (tags)); - - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if ((engine.Capabilities & (ImapCapabilities.Metadata | ImapCapabilities.MetadataServer)) == 0) - throw new NotSupportedException ("The IMAP server does not support the METADATA or METADATA-SERVER extension."); - - var command = new StringBuilder ("GETMETADATA \"\""); - var args = new List (); - bool hasOptions = false; - - if (options.MaxSize.HasValue || options.Depth != 0) { - command.Append (" ("); - if (options.MaxSize.HasValue) - command.AppendFormat ("MAXSIZE {0} ", options.MaxSize.Value); - if (options.Depth > 0) - command.AppendFormat ("DEPTH {0} ", options.Depth == int.MaxValue ? "infinity" : "1"); - command[command.Length - 1] = ')'; - command.Append (' '); - hasOptions = true; - } - - int startIndex = command.Length; - foreach (var tag in tags) { - command.Append (" %S"); - args.Add (tag.Id); - } - - if (hasOptions) { - command[startIndex] = '('; - command.Append (')'); - } - - command.Append ("\r\n"); - - if (args.Count == 0) - return new MetadataCollection (); - - var ic = new ImapCommand (engine, cancellationToken, null, command.ToString (), args.ToArray ()); - ic.RegisterUntaggedHandler ("METADATA", ImapUtils.ParseMetadataAsync); - ic.UserData = new MetadataCollection (); - options.LongEntries = 0; - - engine.QueueCommand (ic); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("GETMETADATA", ic); - - if (ic.RespCodes.Count > 0 && ic.RespCodes[ic.RespCodes.Count - 1].Type == ImapResponseCodeType.Metadata) { - var metadata = (MetadataResponseCode) ic.RespCodes[ic.RespCodes.Count - 1]; - - if (metadata.SubType == MetadataResponseCodeSubType.LongEntries) - options.LongEntries = metadata.Value; - } - - return engine.FilterMetadata ((MetadataCollection) ic.UserData, string.Empty); - } - - /// - /// Gets the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA or METADATA-SERVER extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override MetadataCollection GetMetadata (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (options, tags, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task SetMetadataAsync (MetadataCollection metadata, bool doAsync, CancellationToken cancellationToken) - { - if (metadata == null) - throw new ArgumentNullException (nameof (metadata)); - - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if ((engine.Capabilities & (ImapCapabilities.Metadata | ImapCapabilities.MetadataServer)) == 0) - throw new NotSupportedException ("The IMAP server does not support the METADATA or METADATA-SERVER extension."); - - if (metadata.Count == 0) - return; - - var command = new StringBuilder ("SETMETADATA \"\" ("); - var args = new List (); - - for (int i = 0; i < metadata.Count; i++) { - if (i > 0) - command.Append (' '); - - if (metadata[i].Value != null) { - command.Append ("%S %S"); - args.Add (metadata[i].Tag.Id); - args.Add (metadata[i].Value); - } else { - command.Append ("%S NIL"); - args.Add (metadata[i].Tag.Id); - } - } - command.Append (")\r\n"); - - var ic = new ImapCommand (engine, cancellationToken, null, command.ToString (), args.ToArray ()); - - engine.QueueCommand (ic); - - await engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("SETMETADATA", ic); - } - - /// - /// Sets the specified metadata. - /// - /// - /// Sets the specified metadata. - /// - /// The metadata. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA or METADATA-SERVER extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void SetMetadata (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)) - { - SetMetadataAsync (metadata, false, cancellationToken).GetAwaiter ().GetResult (); - } - - #endregion - - void OnEngineMetadataChanged (object sender, MetadataChangedEventArgs e) - { - OnMetadataChanged (e.Metadata); - } - - void OnEngineFolderCreated (object sender, FolderCreatedEventArgs e) - { - OnFolderCreated (e.Folder); - } - - void OnEngineAlert (object sender, AlertEventArgs e) - { - OnAlert (e.Message); - } - - void OnEngineDisconnected (object sender, EventArgs e) - { - if (connecting) - return; - - var requested = disconnecting; - var uri = engine.Uri; - - disconnecting = false; - secure = false; - - OnDisconnected (uri.Host, uri.Port, GetSecureSocketOptions (uri), requested); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - engine.MetadataChanged -= OnEngineMetadataChanged; - engine.FolderCreated -= OnEngineFolderCreated; - engine.Disconnected -= OnEngineDisconnected; - engine.Alert -= OnEngineAlert; - engine.Dispose (); - disposed = true; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MailKit/Net/Imap/ImapCommand.cs b/src/MailKit/Net/Imap/ImapCommand.cs deleted file mode 100644 index e912692..0000000 --- a/src/MailKit/Net/Imap/ImapCommand.cs +++ /dev/null @@ -1,918 +0,0 @@ -// -// ImapCommand.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; -using MimeKit.IO; -using MimeKit.Utils; - -using SslStream = MailKit.Net.SslStream; -using NetworkStream = MailKit.Net.NetworkStream; - -namespace MailKit.Net.Imap { - /// - /// An IMAP continuation handler. - /// - /// - /// All exceptions thrown by the handler are considered fatal and will - /// force-disconnect the connection. If a non-fatal error occurs, set - /// it on the property. - /// - delegate Task ImapContinuationHandler (ImapEngine engine, ImapCommand ic, string text, bool doAsync); - - /// - /// An IMAP untagged response handler. - /// - /// - /// Most IMAP commands return their results in untagged responses. - /// - delegate Task ImapUntaggedHandler (ImapEngine engine, ImapCommand ic, int index, bool doAsync); - - delegate void ImapCommandResetHandler (ImapCommand ic); - - /// - /// IMAP command status. - /// - enum ImapCommandStatus { - Created, - Queued, - Active, - Complete, - Error - } - - enum ImapLiteralType { - String, - //Stream, - MimeMessage - } - - enum ImapStringType { - Atom, - QString, - Literal, - Nil - } - - /// - /// An IMAP IDLE context. - /// - /// - /// An IMAP IDLE command does not work like normal commands. Unlike most commands, - /// the IDLE command does not end until the client sends a separate "DONE" command. - /// In order to facilitate this, the way this works is that the consumer of MailKit's - /// IMAP APIs provides a 'doneToken' which signals to the command-processing loop to - /// send the "DONE" command. Since, like every other IMAP command, it is also necessary to - /// provide a means of cancelling the IDLE command, it becomes necessary to link the - /// 'doneToken' and the 'cancellationToken' together. - /// - sealed class ImapIdleContext : IDisposable - { - static readonly byte[] DoneCommand = Encoding.ASCII.GetBytes ("DONE\r\n"); - CancellationTokenRegistration registration; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The IMAP engine. - /// The done token. - /// The cancellation token. - public ImapIdleContext (ImapEngine engine, CancellationToken doneToken, CancellationToken cancellationToken) - { - CancellationToken = cancellationToken; - DoneToken = doneToken; - Engine = engine; - } - - /// - /// Get the engine. - /// - /// - /// Gets the engine. - /// - /// The engine. - public ImapEngine Engine { - get; private set; - } - - /// - /// Get the cancellation token. - /// - /// - /// Get the cancellation token. - /// - /// The cancellation token. - public CancellationToken CancellationToken { - get; private set; - } - - /// - /// Get the done token. - /// - /// - /// Gets the done token. - /// - /// The done token. - public CancellationToken DoneToken { - get; private set; - } - -#if false - /// - /// Get whether or not cancellation has been requested. - /// - /// - /// Gets whether or not cancellation has been requested. - /// - /// true if cancellation has been requested; otherwise, false. - public bool IsCancellationRequested { - get { return CancellationToken.IsCancellationRequested; } - } - - /// - /// Get whether or not the IDLE command should be ended. - /// - /// - /// Gets whether or not the IDLE command should be ended. - /// - /// true if the IDLE command should end; otherwise, false. - public bool IsDoneRequested { - get { return DoneToken.IsCancellationRequested; } - } -#endif - - void IdleComplete () - { - if (Engine.State == ImapEngineState.Idle) { - try { - Engine.Stream.Write (DoneCommand, 0, DoneCommand.Length, CancellationToken); - Engine.Stream.Flush (CancellationToken); - } catch { - return; - } - - Engine.State = ImapEngineState.Selected; - } - } - - /// - /// Callback method to be used as the ImapCommand's ContinuationHandler. - /// - /// - /// Callback method to be used as the ImapCommand's ContinuationHandler. - /// - /// The ImapEngine. - /// The ImapCommand. - /// The text. - /// true if the command is being run asynchronously; otherwise, false. - /// - public Task ContinuationHandler (ImapEngine engine, ImapCommand ic, string text, bool doAsync) - { - Engine.State = ImapEngineState.Idle; - - registration = DoneToken.Register (IdleComplete); - - return Task.FromResult (true); - } - - /// - /// Releases all resource used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the - /// so the garbage collector can reclaim the memory that the - /// was occupying. - public void Dispose () - { - registration.Dispose (); - } - } - - /// - /// An IMAP literal object. - /// - /// - /// The literal can be a string, byte[], Stream, or a MimeMessage. - /// - class ImapLiteral - { - public readonly ImapLiteralType Type; - public readonly object Literal; - readonly FormatOptions format; - readonly Action update; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The formatting options. - /// The message. - /// The progress update action. - public ImapLiteral (FormatOptions options, MimeMessage message, Action action = null) - { - format = options.Clone (); - format.NewLineFormat = NewLineFormat.Dos; - - update = action; - - Type = ImapLiteralType.MimeMessage; - Literal = message; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The formatting options. - /// The literal. - public ImapLiteral (FormatOptions options, byte[] literal) - { - format = options.Clone (); - format.NewLineFormat = NewLineFormat.Dos; - - Type = ImapLiteralType.String; - Literal = literal; - } - - /// - /// Get the length of the literal, in bytes. - /// - /// - /// Gets the length of the literal, in bytes. - /// - /// The length. - public long Length { - get { - if (Type == ImapLiteralType.String) - return ((byte[]) Literal).Length; - - using (var measure = new MeasuringStream ()) { - //if (Type == ImapLiteralType.Stream) { - // var stream = (Stream) Literal; - // stream.CopyTo (measure, 4096); - // stream.Position = 0; - - // return measure.Length; - //} - - ((MimeMessage) Literal).WriteTo (format, measure); - - return measure.Length; - } - } - } - - /// - /// Write the literal to the specified stream. - /// - /// - /// Writes the literal to the specified stream. - /// - /// The stream. - /// Whether the literal should be written asynchronously or not. - /// The cancellation token. - public async Task WriteToAsync (ImapStream stream, bool doAsync, CancellationToken cancellationToken) - { - if (Type == ImapLiteralType.String) { - var bytes = (byte[]) Literal; - - if (doAsync) { - await stream.WriteAsync (bytes, 0, bytes.Length, cancellationToken).ConfigureAwait (false); - await stream.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - stream.Write (bytes, 0, bytes.Length, cancellationToken); - stream.Flush (cancellationToken); - } - return; - } - - //if (Type == ImapLiteralType.Stream) { - // var literal = (Stream) Literal; - // var buf = new byte[4096]; - // int nread; - - // if (doAsync) { - // while ((nread = await literal.ReadAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false)) > 0) - // await stream.WriteAsync (buf, 0, nread, cancellationToken).ConfigureAwait (false); - - // await stream.FlushAsync (cancellationToken).ConfigureAwait (false); - // } else { - // while ((nread = literal.Read (buf, 0, buf.Length)) > 0) - // stream.Write (buf, 0, nread, cancellationToken); - - // stream.Flush (cancellationToken); - // } - // return; - //} - - var message = (MimeMessage) Literal; - - using (var s = new ProgressStream (stream, update)) { - if (doAsync) { - await message.WriteToAsync (format, s, cancellationToken).ConfigureAwait (false); - await s.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - message.WriteTo (format, s, cancellationToken); - s.Flush (cancellationToken); - } - } - } - } - - /// - /// A partial IMAP command. - /// - /// - /// IMAP commands that contain literal strings are broken up into multiple parts - /// in case the IMAP server does not support the LITERAL+ extension. These parts - /// are then sent individually as we receive "+" responses from the server. - /// - class ImapCommandPart - { - public readonly byte[] Command; - public readonly ImapLiteral Literal; - public readonly bool WaitForContinuation; - - public ImapCommandPart (byte[] command, ImapLiteral literal, bool wait = true) - { - WaitForContinuation = wait; - Command = command; - Literal = literal; - } - } - - /// - /// An IMAP command. - /// - class ImapCommand - { - static readonly byte[] UTF8LiteralTokenPrefix = Encoding.ASCII.GetBytes ("UTF8 (~{"); - static readonly byte[] LiteralTokenSuffix = { (byte) '}', (byte) '\r', (byte) '\n' }; - static readonly byte[] Nil = { (byte) 'N', (byte) 'I', (byte) 'L' }; - static readonly byte[] NewLine = { (byte) '\r', (byte) '\n' }; - static readonly byte[] LiteralTokenPrefix = { (byte) '{' }; - - public Dictionary UntaggedHandlers { get; private 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 readonly List RespCodes; - 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 bool Bye { get; internal set; } - - readonly List parts = new List (); - readonly ImapEngine Engine; - long totalSize, nwritten; - int current; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The IMAP engine that will be sending the command. - /// The cancellation token. - /// The IMAP folder that the command operates on. - /// The formatting options. - /// The command format. - /// The command arguments. - public ImapCommand (ImapEngine engine, CancellationToken cancellationToken, ImapFolder folder, FormatOptions options, string format, params object[] args) - { - UntaggedHandlers = new Dictionary (StringComparer.OrdinalIgnoreCase); - Logout = format.Equals ("LOGOUT\r\n", StringComparison.Ordinal); - RespCodes = new List (); - CancellationToken = cancellationToken; - Response = ImapCommandResponse.None; - Status = ImapCommandStatus.Created; - Engine = engine; - Folder = folder; - - using (var builder = new MemoryStream ()) { - byte[] buf, utf8 = new byte[8]; - int argc = 0; - string str; - - for (int i = 0; i < format.Length; i++) { - if (format[i] == '%') { - switch (format[++i]) { - case '%': // a literal % - builder.WriteByte ((byte) '%'); - break; - case 'd': // an integer - str = ((int) args[argc++]).ToString (CultureInfo.InvariantCulture); - buf = Encoding.ASCII.GetBytes (str); - builder.Write (buf, 0, buf.Length); - break; - case 'u': // an unsigned integer - str = ((uint) args[argc++]).ToString (CultureInfo.InvariantCulture); - buf = Encoding.ASCII.GetBytes (str); - builder.Write (buf, 0, buf.Length); - break; - case 's': - str = (string) args[argc++]; - buf = Encoding.ASCII.GetBytes (str); - builder.Write (buf, 0, buf.Length); - break; - case 'F': // an ImapFolder - var utf7 = ((ImapFolder) args[argc++]).EncodedName; - AppendString (options, true, builder, utf7); - break; - case 'L': // a MimeMessage or a byte[] - var arg = args[argc++]; - ImapLiteral literal; - byte[] prefix; - - if (arg is MimeMessage message) { - prefix = options.International ? UTF8LiteralTokenPrefix : LiteralTokenPrefix; - literal = new ImapLiteral (options, message, UpdateProgress); - } else { - literal = new ImapLiteral (options, (byte[]) arg); - prefix = LiteralTokenPrefix; - } - - var length = literal.Length; - bool wait = true; - - builder.Write (prefix, 0, prefix.Length); - buf = Encoding.ASCII.GetBytes (length.ToString (CultureInfo.InvariantCulture)); - builder.Write (buf, 0, buf.Length); - - if (CanUseNonSynchronizedLiteral (Engine, length)) { - builder.WriteByte ((byte) '+'); - wait = false; - } - - builder.Write (LiteralTokenSuffix, 0, LiteralTokenSuffix.Length); - - totalSize += length; - - parts.Add (new ImapCommandPart (builder.ToArray (), literal, wait)); - builder.SetLength (0); - - if (prefix == UTF8LiteralTokenPrefix) - builder.WriteByte ((byte) ')'); - break; - case 'S': // a string which may need to be quoted or made into a literal - AppendString (options, true, builder, (string) args[argc++]); - break; - case 'Q': // similar to %S but string must be quoted at a minimum - AppendString (options, false, builder, (string) args[argc++]); - break; - default: - throw new FormatException (); - } - } else if (format[i] < 128) { - builder.WriteByte ((byte) format[i]); - } else { - int nchars = char.IsSurrogate (format[i]) ? 2 : 1; - int nbytes = Encoding.UTF8.GetBytes (format, i, nchars, utf8, 0); - builder.Write (utf8, 0, nbytes); - i += nchars - 1; - } - } - - parts.Add (new ImapCommandPart (builder.ToArray (), null)); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The IMAP engine that will be sending the command. - /// The cancellation token. - /// The IMAP folder that the command operates on. - /// The command format. - /// The command arguments. - public ImapCommand (ImapEngine engine, CancellationToken cancellationToken, ImapFolder folder, string format, params object[] args) - : this (engine, cancellationToken, folder, FormatOptions.Default, format, args) - { - } - - internal static int EstimateCommandLength (ImapEngine engine, FormatOptions options, string format, params object[] args) - { - const int EstimatedTagLength = 10; - var eoln = false; - int length = 0; - int argc = 0; - string str; - - for (int i = 0; i < format.Length; i++) { - if (format[i] == '%') { - switch (format[++i]) { - case '%': // a literal % - length++; - break; - case 'd': // an integer - str = ((int) args[argc++]).ToString (CultureInfo.InvariantCulture); - length += str.Length; - break; - case 'u': // an unsigned integer - str = ((uint) args[argc++]).ToString (CultureInfo.InvariantCulture); - length += str.Length; - break; - case 's': - str = (string) args[argc++]; - length += str.Length; - break; - case 'F': // an ImapFolder - var utf7 = ((ImapFolder) args[argc++]).EncodedName; - length += EstimateStringLength (engine, options, true, utf7, out eoln); - break; - case 'L': // a MimeMessage or a byte[] - var arg = args[argc++]; - byte[] prefix; - long len; - - if (arg is MimeMessage message) { - prefix = options.International ? UTF8LiteralTokenPrefix : LiteralTokenPrefix; - var literal = new ImapLiteral (options, message, null); - len = literal.Length; - } else { - len = ((byte[]) arg).Length; - prefix = LiteralTokenPrefix; - } - - length += prefix.Length; - length += Encoding.ASCII.GetByteCount (len.ToString (CultureInfo.InvariantCulture)); - - if (CanUseNonSynchronizedLiteral (engine, len)) - length++; - - length += LiteralTokenSuffix.Length; - - if (prefix == UTF8LiteralTokenPrefix) - length++; - - eoln = true; - break; - case 'S': // a string which may need to be quoted or made into a literal - length += EstimateStringLength (engine, options, true, (string) args[argc++], out eoln); - break; - case 'Q': // similar to %S but string must be quoted at a minimum - length += EstimateStringLength (engine, options, false, (string) args[argc++], out eoln); - break; - default: - throw new FormatException (); - } - - if (eoln) - break; - } else { - length++; - } - } - - return length + EstimatedTagLength; - } - - internal static int EstimateCommandLength (ImapEngine engine, string format, params object[] args) - { - return EstimateCommandLength (engine, FormatOptions.Default, format, args); - } - - void UpdateProgress (int n) - { - nwritten += n; - - if (Progress != null) - Progress.Report (nwritten, totalSize); - } - - static bool IsAtom (char c) - { - return c < 128 && !char.IsControl (c) && "(){ \t%*\\\"]".IndexOf (c) == -1; - } - - static bool IsQuotedSafe (ImapEngine engine, char c) - { - return (c < 128 || engine.UTF8Enabled) && !char.IsControl (c); - } - - internal static ImapStringType GetStringType (ImapEngine engine, string value, bool allowAtom) - { - var type = allowAtom ? ImapStringType.Atom : ImapStringType.QString; - - if (value == null) - return ImapStringType.Nil; - - if (value.Length == 0) - return ImapStringType.QString; - - for (int i = 0; i < value.Length; i++) { - if (!IsAtom (value[i])) { - if (!IsQuotedSafe (engine, value[i])) - return ImapStringType.Literal; - - type = ImapStringType.QString; - } - } - - return type; - } - - static bool CanUseNonSynchronizedLiteral (ImapEngine engine, long length) - { - return (engine.Capabilities & ImapCapabilities.LiteralPlus) != 0 || - (length <= 4096 && (engine.Capabilities & ImapCapabilities.LiteralMinus) != 0); - } - - static int EstimateStringLength (ImapEngine engine, FormatOptions options, bool allowAtom, string value, out bool eoln) - { - eoln = false; - - switch (GetStringType (engine, value, allowAtom)) { - case ImapStringType.Literal: - var literal = Encoding.UTF8.GetByteCount (value); - var plus = CanUseNonSynchronizedLiteral (engine, literal); - int length = "{}\r\n".Length; - - length += literal.ToString (CultureInfo.InvariantCulture).Length; - if (plus) - length++; - - eoln = true; - - return length++; - case ImapStringType.QString: - return Encoding.UTF8.GetByteCount (MimeUtils.Quote (value)); - case ImapStringType.Nil: - return Nil.Length; - default: - return value.Length; - } - } - - void AppendString (FormatOptions options, bool allowAtom, MemoryStream builder, string value) - { - byte[] buf; - - switch (GetStringType (Engine, value, allowAtom)) { - case ImapStringType.Literal: - var literal = Encoding.UTF8.GetBytes (value); - var plus = CanUseNonSynchronizedLiteral (Engine, literal.Length); - var length = literal.Length.ToString (CultureInfo.InvariantCulture); - buf = Encoding.ASCII.GetBytes (length); - - builder.WriteByte ((byte) '{'); - builder.Write (buf, 0, buf.Length); - if (plus) - builder.WriteByte ((byte) '+'); - builder.WriteByte ((byte) '}'); - builder.WriteByte ((byte) '\r'); - builder.WriteByte ((byte) '\n'); - - if (plus) { - builder.Write (literal, 0, literal.Length); - } else { - parts.Add (new ImapCommandPart (builder.ToArray (), new ImapLiteral (options, literal))); - builder.SetLength (0); - } - break; - case ImapStringType.QString: - buf = Encoding.UTF8.GetBytes (MimeUtils.Quote (value)); - builder.Write (buf, 0, buf.Length); - break; - case ImapStringType.Atom: - buf = Encoding.UTF8.GetBytes (value); - builder.Write (buf, 0, buf.Length); - break; - case ImapStringType.Nil: - builder.Write (Nil, 0, Nil.Length); - break; - } - } - - /// - /// Registers the untagged handler for the specified atom token. - /// - /// The atom token. - /// The handler. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// Untagged handlers must be registered before the command has been queued. - /// - public void RegisterUntaggedHandler (string atom, ImapUntaggedHandler handler) - { - if (atom == null) - throw new ArgumentNullException (nameof (atom)); - - if (handler == null) - throw new ArgumentNullException (nameof (handler)); - - if (Status != ImapCommandStatus.Created) - throw new InvalidOperationException ("Untagged handlers must be registered before the command has been queued."); - - UntaggedHandlers.Add (atom, handler); - } - - /// - /// Sends the next part of the command to the server. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public async Task StepAsync (bool doAsync) - { - var supportsLiteralPlus = (Engine.Capabilities & ImapCapabilities.LiteralPlus) != 0; - var idle = UserData as ImapIdleContext; - var result = ImapCommandResponse.None; - ImapToken token; - - // construct and write the command tag if this is the initial state - if (current == 0) { - Tag = string.Format (CultureInfo.InvariantCulture, "{0}{1:D8}", Engine.TagPrefix, Engine.Tag++); - - var buf = Encoding.ASCII.GetBytes (Tag + " "); - - if (doAsync) - await Engine.Stream.WriteAsync (buf, 0, buf.Length, CancellationToken).ConfigureAwait (false); - else - Engine.Stream.Write (buf, 0, buf.Length, CancellationToken); - } - - do { - var command = parts[current].Command; - - if (doAsync) - await Engine.Stream.WriteAsync (command, 0, command.Length, CancellationToken).ConfigureAwait (false); - else - 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) - break; - - // otherwise, we can write out any and all literal tokens we have... - await parts[current].Literal.WriteToAsync (Engine.Stream, doAsync, CancellationToken).ConfigureAwait (false); - - if (current + 1 >= parts.Count) - break; - - current++; - } while (true); - - if (doAsync) - await Engine.Stream.FlushAsync (CancellationToken).ConfigureAwait (false); - else - Engine.Stream.Flush (CancellationToken); - - // now we need to read the response... - do { - if (Engine.State == ImapEngineState.Idle) { - int timeout = Timeout.Infinite; - - if (Engine.Stream.CanTimeout) { - timeout = Engine.Stream.ReadTimeout; - Engine.Stream.ReadTimeout = Timeout.Infinite; - } - - try { - token = await Engine.ReadTokenAsync (doAsync, CancellationToken).ConfigureAwait (false); - } finally { - if (Engine.Stream.IsConnected && Engine.Stream.CanTimeout) - Engine.Stream.ReadTimeout = timeout; - } - } else { - token = await Engine.ReadTokenAsync (doAsync, CancellationToken).ConfigureAwait (false); - } - - if (token.Type == ImapTokenType.Atom && token.Value.ToString () == "+") { - // we've gotten a continuation response from the server - var text = (await Engine.ReadLineAsync (doAsync, 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, doAsync, CancellationToken).ConfigureAwait (false); - break; - } - - if (ContinuationHandler != null) { - await ContinuationHandler (Engine, this, text, doAsync).ConfigureAwait (false); - } else if (doAsync) { - await Engine.Stream.WriteAsync (NewLine, 0, NewLine.Length, CancellationToken).ConfigureAwait (false); - await Engine.Stream.FlushAsync (CancellationToken).ConfigureAwait (false); - } else { - Engine.Stream.Write (NewLine, 0, NewLine.Length, CancellationToken); - Engine.Stream.Flush (CancellationToken); - } - } else if (token.Type == ImapTokenType.Asterisk) { - // we got an untagged response, let the engine handle this... - await Engine.ProcessUntaggedResponseAsync (doAsync, 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 (doAsync, CancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.Atom, "Syntax error in tagged response. Unexpected token: {0}", token); - - string atom = (string) token.Value; - - switch (atom) { - case "BAD": result = ImapCommandResponse.Bad; break; - case "OK": result = ImapCommandResponse.Ok; break; - case "NO": result = ImapCommandResponse.No; break; - default: throw ImapEngine.UnexpectedToken ("Syntax error in tagged response. Unexpected token: {0}", token); - } - - token = await Engine.ReadTokenAsync (doAsync, CancellationToken).ConfigureAwait (false); - if (token.Type == ImapTokenType.OpenBracket) { - var code = await Engine.ParseResponseCodeAsync (true, doAsync, CancellationToken).ConfigureAwait (false); - RespCodes.Add (code); - break; - } - - if (token.Type != ImapTokenType.Eoln) { - // consume the rest of the line... - var line = await Engine.ReadLineAsync (doAsync, CancellationToken).ConfigureAwait (false); - ResponseText = (((string) token.Value) + line).TrimEnd (); - break; - } - } else if (token.Type == ImapTokenType.OpenBracket) { - // Note: this is a work-around for broken IMAP servers like Office365.com that - // return RESP-CODES that are not preceded by "* OK " such as the example in - // issue #115 (https://github.com/jstedfast/MailKit/issues/115). - var code = await Engine.ParseResponseCodeAsync (false, doAsync, CancellationToken).ConfigureAwait (false); - RespCodes.Add (code); - } else { - // no clue what we got... - throw ImapEngine.UnexpectedToken ("Syntax error in response. Unexpected token: {0}", token); - } - } while (Status == ImapCommandStatus.Active); - - if (Status == ImapCommandStatus.Active) { - current++; - - if (current >= parts.Count || result != ImapCommandResponse.None) { - Status = ImapCommandStatus.Complete; - Response = result; - return false; - } - - return true; - } - - return false; - } - } -} diff --git a/src/MailKit/Net/Imap/ImapCommandException.cs b/src/MailKit/Net/Imap/ImapCommandException.cs deleted file mode 100644 index 1cc2684..0000000 --- a/src/MailKit/Net/Imap/ImapCommandException.cs +++ /dev/null @@ -1,190 +0,0 @@ -// -// ImapCommandException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit.Net.Imap { - /// - /// An exception that is thrown when an IMAP command returns NO or BAD. - /// - /// - /// The exception that is thrown when an IMAP command fails. Unlike a , - /// a does not require the to be reconnected. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class ImapCommandException : CommandException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the serialized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected ImapCommandException (SerializationInfo info, StreamingContext context) : base (info, context) - { - Response = (ImapCommandResponse) info.GetValue ("Response", typeof (ImapCommandResponse)); - ResponseText = info.GetString ("ResponseText"); - } -#endif - - /// - /// Create a new based on the specified command name and state. - /// - /// - /// Create a new based on the specified command name and state. - /// - /// A new command exception. - /// The command name. - /// The command state. - internal static ImapCommandException Create (string command, ImapCommand ic) - { - var result = ic.Response.ToString ().ToUpperInvariant (); - string message, reason = 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)) { - reason = ic.RespCodes[i].Message; - break; - } - } - } else { - reason = ic.ResponseText; - } - - if (!string.IsNullOrEmpty (reason)) - message = string.Format ("The IMAP server replied to the '{0}' command with a '{1}' response: {2}", command, result, reason); - else - message = string.Format ("The IMAP server replied to the '{0}' command with a '{1}' response.", command, result); - - return ic.Exception != null ? new ImapCommandException (ic.Response, reason, message, ic.Exception) : new ImapCommandException (ic.Response, reason, message); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The IMAP command response. - /// The error message. - /// The human-readable response text. - /// The inner exception. - public ImapCommandException (ImapCommandResponse response, string responseText, string message, Exception innerException) : base (message, innerException) - { - ResponseText = responseText; - Response = response; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The IMAP command response. - /// The human-readable response text. - /// The error message. - public ImapCommandException (ImapCommandResponse response, string responseText, string message) : base (message) - { - ResponseText = responseText; - Response = response; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The IMAP command response. - /// The human-readable response text. - public ImapCommandException (ImapCommandResponse response, string responseText) - { - ResponseText = responseText; - Response = response; - } - - /// - /// Gets the IMAP command response. - /// - /// - /// Gets the IMAP command response. - /// - /// The IMAP command response. - public ImapCommandResponse Response { - get; private set; - } - - /// - /// Gets the human-readable IMAP command response text. - /// - /// - /// Gets the human-readable IMAP command response text. - /// - /// The response text. - public string ResponseText { - get; private set; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Serializes the state of the . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("Response", Response, typeof (ImapCommandResponse)); - info.AddValue ("ResponseText", ResponseText); - } -#endif - } -} diff --git a/src/MailKit/Net/Imap/ImapCommandResponse.cs b/src/MailKit/Net/Imap/ImapCommandResponse.cs deleted file mode 100644 index 1a38ad4..0000000 --- a/src/MailKit/Net/Imap/ImapCommandResponse.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// ImapCommandResult.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Net.Imap { - /// - /// An enumeration of possible IMAP command responses. - /// - /// - /// An enumeration of possible IMAP command responses. - /// - public enum ImapCommandResponse { - /// - /// No IMAP command response yet. - /// - None, - - /// - /// The command resulted in an "OK" response. - /// - Ok, - - /// - /// The command resulted in a "NO" response. - /// - No, - - /// - /// The command resulted in a "BAD" response. - /// - Bad - } -} diff --git a/src/MailKit/Net/Imap/ImapEncoding.cs b/src/MailKit/Net/Imap/ImapEncoding.cs deleted file mode 100644 index 831c64f..0000000 --- a/src/MailKit/Net/Imap/ImapEncoding.cs +++ /dev/null @@ -1,155 +0,0 @@ -// -// ImapEncoding.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Text; - -namespace MailKit.Net.Imap { - static class ImapEncoding - { - const string utf7_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; - - static readonly byte[] utf7_rank = { - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255, 62, 63,255,255,255, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255, - 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, - 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, - }; - - public static string Decode (string text) - { - var decoded = new StringBuilder (); - bool shifted = false; - int bits = 0, v = 0; - int index = 0; - char c; - - while (index < text.Length) { - c = text[index++]; - - if (shifted) { - if (c == '-') { - // shifted back out of modified UTF-7 - shifted = false; - bits = v = 0; - } else if (c > 127) { - // invalid UTF-7 - return text; - } else { - byte rank = utf7_rank[(byte) c]; - - if (rank == 0xff) { - // invalid UTF-7 - return text; - } - - v = (v << 6) | rank; - bits += 6; - - if (bits >= 16) { - char u = (char) ((v >> (bits - 16)) & 0xffff); - decoded.Append (u); - bits -= 16; - } - } - } else if (c == '&' && index < text.Length) { - if (text[index] == '-') { - decoded.Append ('&'); - index++; - } else { - // shifted into modified UTF-7 - shifted = true; - } - } else { - decoded.Append (c); - } - } - - return decoded.ToString (); - } - - static void Utf7ShiftOut (StringBuilder output, int u, int bits) - { - if (bits > 0) { - int x = (u << (6 - bits)) & 0x3f; - output.Append (utf7_alphabet[x]); - } - - output.Append ('-'); - } - - public static string Encode (string text) - { - var encoded = new StringBuilder (); - bool shifted = false; - int bits = 0, u = 0; - - for (int index = 0; index < text.Length; index++) { - char c = text[index]; - - if (c >= 0x20 && c < 0x7f) { - // characters with octet values 0x20-0x25 and 0x27-0x7e - // represent themselves while 0x26 ("&") is represented - // by the two-octet sequence "&-" - - if (shifted) { - Utf7ShiftOut (encoded, u, bits); - shifted = false; - bits = 0; - } - - if (c == 0x26) - encoded.Append ("&-"); - else - encoded.Append (c); - } else { - // base64 encode - if (!shifted) { - encoded.Append ('&'); - shifted = true; - } - - u = (u << 16) | (c & 0xffff); - bits += 16; - - while (bits >= 6) { - int x = (u >> (bits - 6)) & 0x3f; - encoded.Append (utf7_alphabet[x]); - bits -= 6; - } - } - } - - if (shifted) - Utf7ShiftOut (encoded, u, bits); - - return encoded.ToString (); - } - } -} diff --git a/src/MailKit/Net/Imap/ImapEngine.cs b/src/MailKit/Net/Imap/ImapEngine.cs deleted file mode 100644 index 6765c35..0000000 --- a/src/MailKit/Net/Imap/ImapEngine.cs +++ /dev/null @@ -1,2905 +0,0 @@ -// -// ImapEngine.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Diagnostics; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -namespace MailKit.Net.Imap { - delegate ImapFolder CreateImapFolderDelegate (ImapFolderConstructorArgs args); - - /// - /// The state of the . - /// - enum ImapEngineState { - /// - /// The ImapEngine is in the disconnected state. - /// - Disconnected, - - /// - /// The ImapEngine is in the process of connecting. - /// - Connecting, - - /// - /// The ImapEngine is connected but not yet authenticated. - /// - Connected, - - /// - /// The ImapEngine is in the authenticated state. - /// - Authenticated, - - /// - /// The ImapEngine is in the selected state. - /// - Selected, - - /// - /// The ImapEngine is in the IDLE state. - /// - Idle - } - - enum ImapProtocolVersion { - Unknown, - IMAP4, - IMAP4rev1 - } - - enum ImapUntaggedResult { - Ok, - No, - Bad, - Handled - } - - enum ImapQuirksMode { - None, - Courier, - Cyrus, - Domino, - Dovecot, - Exchange, - GMail, - ProtonMail, - SmarterMail, - SunMicrosystems, - UW, - Yahoo, - Yandex - } - - class ImapFolderNameComparer : IEqualityComparer - { - public char DirectorySeparator; - - public ImapFolderNameComparer (char directorySeparator) - { - DirectorySeparator = directorySeparator; - } - - public bool Equals (string x, string y) - { - x = ImapUtils.CanonicalizeMailboxName (x, DirectorySeparator); - y = ImapUtils.CanonicalizeMailboxName (y, DirectorySeparator); - - return x == y; - } - - public int GetHashCode (string obj) - { - return ImapUtils.CanonicalizeMailboxName (obj, DirectorySeparator).GetHashCode (); - } - } - - /// - /// An IMAP command engine. - /// - class ImapEngine : IDisposable - { - internal const string GenericUntaggedResponseSyntaxErrorFormat = "Syntax error in untagged {0} response. Unexpected token: {1}"; - internal const string GenericItemSyntaxErrorFormat = "Syntax error in {0}. Unexpected token: {1}"; - internal const string FetchBodySyntaxErrorFormat = "Syntax error in BODY. Unexpected token: {0}"; - const string GenericResponseCodeSyntaxErrorFormat = "Syntax error in {0} response code. Unexpected token: {1}"; - const string GreetingSyntaxErrorFormat = "Syntax error in IMAP server greeting. Unexpected token: {0}"; - - internal static readonly Encoding Latin1; - internal static readonly Encoding UTF8; - static int TagPrefixIndex; - - internal readonly Dictionary FolderCache; - readonly CreateImapFolderDelegate createImapFolder; - readonly ImapFolderNameComparer cacheComparer; - internal ImapQuirksMode QuirksMode; - readonly List queue; - internal char TagPrefix; - ImapCommand current; - MimeParser parser; - internal int Tag; - bool disposed; - - static ImapEngine () - { - UTF8 = Encoding.GetEncoding (65001, new EncoderExceptionFallback (), new DecoderExceptionFallback ()); - - try { - Latin1 = Encoding.GetEncoding (28591); - } catch (NotSupportedException) { - Latin1 = Encoding.GetEncoding (1252); - } - } - - public ImapEngine (CreateImapFolderDelegate createImapFolderDelegate) - { - cacheComparer = new ImapFolderNameComparer ('.'); - - FolderCache = new Dictionary (cacheComparer); - ThreadingAlgorithms = new HashSet (); - AuthenticationMechanisms = new HashSet (StringComparer.Ordinal); - CompressionAlgorithms = new HashSet (StringComparer.Ordinal); - SupportedContexts = new HashSet (StringComparer.Ordinal); - SupportedCharsets = new HashSet (StringComparer.OrdinalIgnoreCase); - Rights = new AccessRights (); - - PersonalNamespaces = new FolderNamespaceCollection (); - SharedNamespaces = new FolderNamespaceCollection (); - OtherNamespaces = new FolderNamespaceCollection (); - - ProtocolVersion = ImapProtocolVersion.Unknown; - createImapFolder = createImapFolderDelegate; - Capabilities = ImapCapabilities.None; - QuirksMode = ImapQuirksMode.None; - queue = new List (); - } - - /// - /// Get the authentication mechanisms supported by the IMAP server. - /// - /// - /// The authentication mechanisms are queried durring the - /// method. - /// - /// The authentication mechanisms. - public HashSet AuthenticationMechanisms { - get; private set; - } - - /// - /// Get the compression algorithms supported by the IMAP server. - /// - /// - /// The compression algorithms are populated by the - /// method. - /// - /// The compression algorithms. - public HashSet CompressionAlgorithms { - get; private set; - } - - /// - /// Get the threading algorithms supported by the IMAP server. - /// - /// - /// The threading algorithms are populated by the - /// method. - /// - /// The threading algorithms. - public HashSet ThreadingAlgorithms { - get; private set; - } - - /// - /// Gets the append limit supported by the IMAP server. - /// - /// - /// Gets the append limit supported by the IMAP server. - /// - /// The append limit. - public uint? AppendLimit { - get; private set; - } - - /// - /// Gets the I18NLEVEL supported by the IMAP server. - /// - /// - /// Gets the I18NLEVEL supported by the IMAP server. - /// - /// The internationalization level. - public int I18NLevel { - get; private set; - } - - /// - /// Get the capabilities supported by the IMAP server. - /// - /// - /// The capabilities will not be known until a successful connection - /// has been made via the method. - /// - /// The capabilities. - public ImapCapabilities Capabilities { - get; set; - } - - /// - /// Indicates whether or not the engine is busy processing commands. - /// - /// - /// Indicates whether or not the engine is busy processing commands. - /// - /// true if th e engine is busy processing commands; otherwise, false. - internal bool IsBusy { - get { return current != null; } - } - - /// - /// Get the capabilities version. - /// - /// - /// Every time the engine receives an untagged CAPABILITIES - /// response from the server, it increments this value. - /// - /// The capabilities version. - public int CapabilitiesVersion { - get; private set; - } - - /// - /// Get the IMAP protocol version. - /// - /// - /// Gets the IMAP protocol version. - /// - /// The IMAP protocol version. - public ImapProtocolVersion ProtocolVersion { - get; private set; - } - - /// - /// Get the rights specified in the capabilities. - /// - /// - /// Gets the rights specified in the capabilities. - /// - /// The rights. - public AccessRights Rights { - get; private set; - } - - /// - /// Get the supported charsets. - /// - /// - /// Gets the supported charsets. - /// - /// The supported charsets. - public HashSet SupportedCharsets { - get; private set; - } - - /// - /// Get the supported contexts. - /// - /// - /// Gets the supported contexts. - /// - /// The supported contexts. - public HashSet SupportedContexts { - get; private set; - } - - /// - /// Get whether or not the QRESYNC feature has been enabled. - /// - /// - /// Gets whether or not the QRESYNC feature has been enabled. - /// - /// true if the QRESYNC feature has been enabled; otherwise, false. - public bool QResyncEnabled { - get; internal set; - } - - /// - /// Get whether or not the UTF8=ACCEPT feature has been enabled. - /// - /// - /// Gets whether or not the UTF8=ACCEPT feature has been enabled. - /// - /// true if the UTF8=ACCEPT feature has been enabled; otherwise, false. - public bool UTF8Enabled { - get; internal set; - } - - /// - /// Get the URI of the IMAP server. - /// - /// - /// Gets the URI of the IMAP server. - /// - /// The URI of the IMAP server. - public Uri Uri { - get; internal set; - } - - /// - /// Get the underlying IMAP stream. - /// - /// - /// Gets the underlying IMAP stream. - /// - /// The IMAP stream. - public ImapStream Stream { - get; private set; - } - - /// - /// Get or sets the state of the engine. - /// - /// - /// Gets or sets the state of the engine. - /// - /// The engine state. - public ImapEngineState State { - get; internal set; - } - - /// - /// Get whether or not the engine is currently connected to a IMAP server. - /// - /// - /// Gets whether or not the engine is currently connected to a IMAP server. - /// - /// true if the engine is connected; otherwise, false. - public bool IsConnected { - get { return Stream != null && Stream.IsConnected; } - } - - /// - /// Gets the personal folder namespaces. - /// - /// - /// Gets the personal folder namespaces. - /// - /// The personal folder namespaces. - public FolderNamespaceCollection PersonalNamespaces { - get; private set; - } - - /// - /// Gets the shared folder namespaces. - /// - /// - /// Gets the shared folder namespaces. - /// - /// The shared folder namespaces. - public FolderNamespaceCollection SharedNamespaces { - get; private set; - } - - /// - /// Gets the other folder namespaces. - /// - /// - /// Gets the other folder namespaces. - /// - /// The other folder namespaces. - public FolderNamespaceCollection OtherNamespaces { - get; private set; - } - - /// - /// Gets the selected folder. - /// - /// - /// Gets the selected folder. - /// - /// The selected folder. - public ImapFolder Selected { - get; internal set; - } - - /// - /// Gets a value indicating whether the engine is disposed. - /// - /// - /// Gets a value indicating whether the engine is disposed. - /// - /// true if the engine is disposed; otherwise, false. - public bool IsDisposed { - get { return disposed; } - } - - /// - /// Gets whether the current NOTIFY status prevents using indexes and * for referencing messages. - /// - /// - /// Gets whether the current NOTIFY status prevents using indexes and * for referencing messages. This is the case when the client has asked for MessageNew or MessageExpunge events on the SELECTED mailbox. - /// - /// true if the use of indexes and * is prevented; otherwise, false. - internal bool NotifySelectedNewExpunge { - get; set; - } - - #region Special Folders - - /// - /// Gets the Inbox folder. - /// - /// The Inbox folder. - public ImapFolder Inbox { - get; private set; - } - - /// - /// Gets the special folder containing an aggregate of all messages. - /// - /// The folder containing all messages. - public ImapFolder All { - get; private set; - } - - /// - /// Gets the special archive folder. - /// - /// The archive folder. - public ImapFolder Archive { - get; private set; - } - - /// - /// Gets the special folder containing drafts. - /// - /// The drafts folder. - public ImapFolder Drafts { - get; private set; - } - - /// - /// Gets the special folder containing flagged messages. - /// - /// The flagged folder. - public ImapFolder Flagged { - get; private set; - } - - /// - /// Gets the special folder containing important messages. - /// - /// The important folder. - public ImapFolder Important { - get; private set; - } - - /// - /// Gets the special folder containing junk messages. - /// - /// The junk folder. - public ImapFolder Junk { - get; private set; - } - - /// - /// Gets the special folder containing sent messages. - /// - /// The sent. - public ImapFolder Sent { - get; private set; - } - - /// - /// Gets the folder containing deleted messages. - /// - /// The trash folder. - public ImapFolder Trash { - get; private set; - } - - #endregion - - internal ImapFolder CreateImapFolder (string encodedName, FolderAttributes attributes, char delim) - { - var args = new ImapFolderConstructorArgs (this, encodedName, attributes, delim); - - return createImapFolder (args); - } - - internal static ImapProtocolException UnexpectedToken (string format, params object[] args) - { - return new ImapProtocolException (string.Format (CultureInfo.InvariantCulture, format, args)) { UnexpectedToken = true }; - } - - internal static void AssertToken (ImapToken token, ImapTokenType type, string format, params object[] args) - { - if (token.Type != type) - throw UnexpectedToken (format, args); - } - - internal static void AssertToken (ImapToken token, ImapTokenType type1, ImapTokenType type2, string format, params object[] args) - { - if (token.Type != type1 && token.Type != type2) - throw UnexpectedToken (format, args); - } - - internal static uint ParseNumber (ImapToken token, bool nonZero, string format, params object[] args) - { - uint value; - - AssertToken (token, ImapTokenType.Atom, format, args); - - if (!uint.TryParse ((string) token.Value, NumberStyles.None, CultureInfo.InvariantCulture, out value) || (nonZero && value == 0)) - throw UnexpectedToken (format, args); - - return value; - } - - internal static ulong ParseNumber64 (ImapToken token, bool nonZero, string format, params object[] args) - { - ulong value; - - AssertToken (token, ImapTokenType.Atom, format, args); - - if (!ulong.TryParse ((string) token.Value, NumberStyles.None, CultureInfo.InvariantCulture, out value) || (nonZero && value == 0)) - throw UnexpectedToken (format, args); - - return value; - } - - internal static UniqueIdSet ParseUidSet (ImapToken token, uint validity, string format, params object[] args) - { - UniqueIdSet uids; - - AssertToken (token, ImapTokenType.Atom, format, args); - - if (!UniqueIdSet.TryParse ((string) token.Value, validity, out uids)) - throw UnexpectedToken (format, args); - - return uids; - } - - /// - /// Sets the stream - this is only here to be used by the unit tests. - /// - /// The IMAP stream. - internal void SetStream (ImapStream stream) - { - Stream = stream; - } - - /// - /// Takes posession of the and reads the greeting. - /// - /// The IMAP stream. - /// Whether or not asyncrhonois IO methods should be used. - /// The cancellation token. - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public async Task ConnectAsync (ImapStream stream, bool doAsync, CancellationToken cancellationToken) - { - TagPrefix = (char) ('A' + (TagPrefixIndex++ % 26)); - ProtocolVersion = ImapProtocolVersion.Unknown; - Capabilities = ImapCapabilities.None; - AuthenticationMechanisms.Clear (); - CompressionAlgorithms.Clear (); - ThreadingAlgorithms.Clear (); - SupportedCharsets.Clear (); - SupportedContexts.Clear (); - Rights.Clear (); - - State = ImapEngineState.Connecting; - QuirksMode = ImapQuirksMode.None; - SupportedCharsets.Add ("US-ASCII"); - SupportedCharsets.Add ("UTF-8"); - CapabilitiesVersion = 0; - QResyncEnabled = false; - UTF8Enabled = false; - AppendLimit = null; - Selected = null; - Stream = stream; - I18NLevel = 0; - Tag = 0; - - try { - var token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Asterisk, GreetingSyntaxErrorFormat, token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Atom, GreetingSyntaxErrorFormat, token); - - var atom = (string) token.Value; - var text = string.Empty; - var state = State; - var bye = false; - - switch (atom.ToUpperInvariant ()) { - case "BYE": - bye = true; - break; - case "PREAUTH": - state = ImapEngineState.Authenticated; - break; - case "OK": - state = ImapEngineState.Connected; - break; - default: - throw UnexpectedToken (GreetingSyntaxErrorFormat, token); - } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenBracket) { - var code = await ParseResponseCodeAsync (false, doAsync, cancellationToken).ConfigureAwait (false); - if (code.Type == ImapResponseCodeType.Alert) { - OnAlert (code.Message); - - if (bye) - throw new ImapProtocolException (code.Message); - } else { - text = code.Message; - } - } else if (token.Type != ImapTokenType.Eoln) { - text = (string) token.Value; - text += await ReadLineAsync (doAsync, cancellationToken).ConfigureAwait (false); - text = text.TrimEnd (); - - if (bye) - throw new ImapProtocolException (text); - } else if (bye) { - throw new ImapProtocolException ("The IMAP server unexpectedly refused the connection."); - } - - if (text.StartsWith ("Courier-IMAP ready.", StringComparison.Ordinal)) - QuirksMode = ImapQuirksMode.Courier; - else if (text.Contains (" Cyrus IMAP ")) - QuirksMode = ImapQuirksMode.Cyrus; - else if (text.StartsWith ("Domino IMAP4 Server", StringComparison.Ordinal)) - QuirksMode = ImapQuirksMode.Domino; - else if (text.StartsWith ("Dovecot ready.", StringComparison.Ordinal)) - QuirksMode = ImapQuirksMode.Dovecot; - else if (text.StartsWith ("Microsoft Exchange Server 2007 IMAP4 service is ready", StringComparison.Ordinal)) - QuirksMode = ImapQuirksMode.Exchange; - else if (text.StartsWith ("The Microsoft Exchange IMAP4 service is ready.", StringComparison.Ordinal)) - QuirksMode = ImapQuirksMode.Exchange; - else if (text.StartsWith ("Gimap ready", StringComparison.Ordinal)) - QuirksMode = ImapQuirksMode.GMail; - else if (text.Contains (" IMAP4rev1 2007f.") || text.Contains (" Panda IMAP ")) - QuirksMode = ImapQuirksMode.UW; - else if (text.Contains ("SmarterMail")) - QuirksMode = ImapQuirksMode.SmarterMail; - else if (text.Contains ("Yandex IMAP4rev1 ")) - QuirksMode = ImapQuirksMode.Yandex; - - State = state; - } catch { - Disconnect (); - throw; - } - } - - /// - /// Disconnects the . - /// - /// - /// Disconnects the . - /// - public void Disconnect () - { - if (Selected != null) { - Selected.Reset (); - Selected.OnClosed (); - Selected = null; - } - - current = null; - - if (Stream != null) { - Stream.Dispose (); - Stream = null; - } - - if (State != ImapEngineState.Disconnected) { - State = ImapEngineState.Disconnected; - OnDisconnected (); - } - } - - internal async Task ReadLineAsync (bool doAsync, CancellationToken cancellationToken) - { - using (var memory = new MemoryStream ()) { - bool complete; - byte[] buf; - int count; - - do { - if (doAsync) - complete = await Stream.ReadLineAsync (memory, cancellationToken).ConfigureAwait (false); - else - complete = Stream.ReadLine (memory, cancellationToken); - } while (!complete); - - count = (int) memory.Length; -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - buf = memory.GetBuffer (); -#else - buf = memory.ToArray (); -#endif - - try { - return UTF8.GetString (buf, 0, count); - } catch (DecoderFallbackException) { - return Latin1.GetString (buf, 0, count); - } - } - } - -#if false - /// - /// Reads a single line from the . - /// - /// The line. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public string ReadLine (CancellationToken cancellationToken) - { - return ReadLineAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously reads a single line from the . - /// - /// The line. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public Task ReadLineAsync (CancellationToken cancellationToken) - { - return ReadLineAsync (true, cancellationToken); - } -#endif - - internal Task ReadTokenAsync (string specials, bool doAsync, CancellationToken cancellationToken) - { - return Stream.ReadTokenAsync (specials, doAsync, cancellationToken); - } - - internal Task ReadTokenAsync (bool doAsync, CancellationToken cancellationToken) - { - return Stream.ReadTokenAsync (ImapStream.DefaultSpecials, doAsync, cancellationToken); - } - - internal async Task PeekTokenAsync (string specials, bool doAsync, CancellationToken cancellationToken) - { - var token = await ReadTokenAsync (specials, doAsync, cancellationToken).ConfigureAwait (false); - - Stream.UngetToken (token); - - return token; - } - - internal Task PeekTokenAsync (bool doAsync, CancellationToken cancellationToken) - { - return PeekTokenAsync (ImapStream.DefaultSpecials, doAsync, cancellationToken); - } - - /// - /// Reads the next token. - /// - /// The token. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public ImapToken ReadToken (CancellationToken cancellationToken) - { - return Stream.ReadToken (cancellationToken); - } - -#if false - /// - /// Asynchronously reads the next token. - /// - /// The token. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public Task ReadTokenAsync (CancellationToken cancellationToken) - { - return Stream.ReadTokenAsync (cancellationToken); - } - - /// - /// Peeks at the next token. - /// - /// The next token. - /// A list of characters that are not legal in bare string tokens. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public ImapToken PeekToken (string specials, CancellationToken cancellationToken) - { - return PeekTokenAsync (specials, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously peeks at the next token. - /// - /// The next token. - /// A list of characters that are not legal in bare string tokens. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public Task PeekTokenAsync (string specials, CancellationToken cancellationToken) - { - return PeekTokenAsync (specials, true, cancellationToken); - } - - /// - /// Peeks at the next token. - /// - /// The next token. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public ImapToken PeekToken (CancellationToken cancellationToken) - { - return PeekTokenAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously peeks at the next token. - /// - /// The next token. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An IMAP protocol error occurred. - /// - public Task PeekTokenAsync (CancellationToken cancellationToken) - { - return PeekTokenAsync (true, cancellationToken); - } -#endif - - internal async Task ReadLiteralAsync (bool doAsync, CancellationToken cancellationToken) - { - if (Stream.Mode != ImapStreamMode.Literal) - throw new InvalidOperationException (); - - using (var memory = new MemoryStream (Stream.LiteralLength)) { - var buf = new byte[4096]; - int nread; - - if (doAsync) { - while ((nread = await Stream.ReadAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false)) > 0) - memory.Write (buf, 0, nread); - } else { - while ((nread = Stream.Read (buf, 0, buf.Length, cancellationToken)) > 0) - memory.Write (buf, 0, nread); - } - - nread = (int) memory.Length; -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - buf = memory.GetBuffer (); -#else - buf = memory.ToArray (); -#endif - - return Latin1.GetString (buf, 0, nread); - } - } - -#if false - /// - /// Reads the literal as a string. - /// - /// The literal. - /// The cancellation token. - /// - /// The is not in literal mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public string ReadLiteral (CancellationToken cancellationToken) - { - return ReadLiteralAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously reads the literal as a string. - /// - /// The literal. - /// The cancellation token. - /// - /// The is not in literal mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task ReadLiteralAsync (CancellationToken cancellationToken) - { - return ReadLiteralAsync (true, cancellationToken); - } -#endif - - async Task SkipLineAsync (bool doAsync, CancellationToken cancellationToken) - { - ImapToken token; - - do { - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Literal) { - var buf = new byte[4096]; - int nread; - - do { - if (doAsync) - nread = await Stream.ReadAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false); - else - nread = Stream.Read (buf, 0, buf.Length, cancellationToken); - } while (nread > 0); - } - } while (token.Type != ImapTokenType.Eoln); - } - - async Task UpdateCapabilitiesAsync (ImapTokenType sentinel, bool doAsync, CancellationToken cancellationToken) - { - // Clear the extensions except STARTTLS so that this capability stays set after a STARTTLS command. - ProtocolVersion = ImapProtocolVersion.Unknown; - Capabilities &= ImapCapabilities.StartTLS; - AuthenticationMechanisms.Clear (); - CompressionAlgorithms.Clear (); - ThreadingAlgorithms.Clear (); - SupportedContexts.Clear (); - CapabilitiesVersion++; - AppendLimit = null; - Rights.Clear (); - I18NLevel = 0; - - var token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - while (token.Type == ImapTokenType.Atom) { - var atom = (string) token.Value; - - if (atom.StartsWith ("AUTH=", StringComparison.OrdinalIgnoreCase)) { - AuthenticationMechanisms.Add (atom.Substring ("AUTH=".Length)); - } else if (atom.StartsWith ("APPENDLIMIT=", StringComparison.OrdinalIgnoreCase)) { - uint limit; - - if (uint.TryParse (atom.Substring ("APPENDLIMIT=".Length), NumberStyles.None, CultureInfo.InvariantCulture, out limit)) - AppendLimit = limit; - - Capabilities |= ImapCapabilities.AppendLimit; - } else if (atom.StartsWith ("COMPRESS=", StringComparison.OrdinalIgnoreCase)) { - CompressionAlgorithms.Add (atom.Substring ("COMPRESS=".Length)); - Capabilities |= ImapCapabilities.Compress; - } else if (atom.StartsWith ("CONTEXT=", StringComparison.OrdinalIgnoreCase)) { - SupportedContexts.Add (atom.Substring ("CONTEXT=".Length)); - Capabilities |= ImapCapabilities.Context; - } else if (atom.StartsWith ("I18NLEVEL=", StringComparison.OrdinalIgnoreCase)) { - int level; - - int.TryParse (atom.Substring ("I18NLEVEL=".Length), NumberStyles.None, CultureInfo.InvariantCulture, out level); - I18NLevel = level; - - Capabilities |= ImapCapabilities.I18NLevel; - } else if (atom.StartsWith ("RIGHTS=", StringComparison.OrdinalIgnoreCase)) { - var rights = atom.Substring ("RIGHTS=".Length); - Rights.AddRange (rights); - } else if (atom.StartsWith ("THREAD=", StringComparison.OrdinalIgnoreCase)) { - var algorithm = atom.Substring ("THREAD=".Length); - switch (algorithm.ToUpperInvariant ()) { - case "ORDEREDSUBJECT": - ThreadingAlgorithms.Add (ThreadingAlgorithm.OrderedSubject); - break; - case "REFERENCES": - ThreadingAlgorithms.Add (ThreadingAlgorithm.References); - break; - } - - Capabilities |= ImapCapabilities.Thread; - } else { - switch (atom.ToUpperInvariant ()) { - case "IMAP4": Capabilities |= ImapCapabilities.IMAP4; break; - case "IMAP4REV1": Capabilities |= ImapCapabilities.IMAP4rev1; break; - case "STATUS": Capabilities |= ImapCapabilities.Status; break; - case "ACL": Capabilities |= ImapCapabilities.Acl; break; - case "QUOTA": Capabilities |= ImapCapabilities.Quota; break; - case "LITERAL+": Capabilities |= ImapCapabilities.LiteralPlus; break; - case "IDLE": Capabilities |= ImapCapabilities.Idle; break; - case "MAILBOX-REFERRALS": Capabilities |= ImapCapabilities.MailboxReferrals; break; - case "LOGIN-REFERRALS": Capabilities |= ImapCapabilities.LoginReferrals; break; - case "NAMESPACE": Capabilities |= ImapCapabilities.Namespace; break; - case "ID": Capabilities |= ImapCapabilities.Id; break; - case "CHILDREN": Capabilities |= ImapCapabilities.Children; break; - case "LOGINDISABLED": Capabilities |= ImapCapabilities.LoginDisabled; break; - case "STARTTLS": Capabilities |= ImapCapabilities.StartTLS; break; - case "MULTIAPPEND": Capabilities |= ImapCapabilities.MultiAppend; break; - case "BINARY": Capabilities |= ImapCapabilities.Binary; break; - case "UNSELECT": Capabilities |= ImapCapabilities.Unselect; break; - case "UIDPLUS": Capabilities |= ImapCapabilities.UidPlus; break; - case "CATENATE": Capabilities |= ImapCapabilities.Catenate; break; - case "CONDSTORE": Capabilities |= ImapCapabilities.CondStore; break; - case "ESEARCH": Capabilities |= ImapCapabilities.ESearch; break; - case "SASL-IR": Capabilities |= ImapCapabilities.SaslIR; break; - case "WITHIN": Capabilities |= ImapCapabilities.Within; break; - case "ENABLE": Capabilities |= ImapCapabilities.Enable; break; - case "QRESYNC": Capabilities |= ImapCapabilities.QuickResync; break; - case "SEARCHRES": Capabilities |= ImapCapabilities.SearchResults; break; - case "SORT": Capabilities |= ImapCapabilities.Sort; break; - case "ANNOTATE-EXPERIMENT-1": Capabilities |= ImapCapabilities.Annotate; break; - case "LIST-EXTENDED": Capabilities |= ImapCapabilities.ListExtended; break; - case "CONVERT": Capabilities |= ImapCapabilities.Convert; break; - case "LANGUAGE": Capabilities |= ImapCapabilities.Language; break; - case "ESORT": Capabilities |= ImapCapabilities.ESort; break; - case "METADATA": Capabilities |= ImapCapabilities.Metadata; break; - case "METADATA-SERVER": Capabilities |= ImapCapabilities.MetadataServer; break; - case "NOTIFY": Capabilities |= ImapCapabilities.Notify; break; - case "LIST-STATUS": Capabilities |= ImapCapabilities.ListStatus; break; - case "SORT=DISPLAY": Capabilities |= ImapCapabilities.SortDisplay; break; - case "CREATE-SPECIAL-USE": Capabilities |= ImapCapabilities.CreateSpecialUse; break; - case "SPECIAL-USE": Capabilities |= ImapCapabilities.SpecialUse; break; - case "SEARCH=FUZZY": Capabilities |= ImapCapabilities.FuzzySearch; break; - case "MULTISEARCH": Capabilities |= ImapCapabilities.MultiSearch; break; - case "MOVE": Capabilities |= ImapCapabilities.Move; break; - case "UTF8=ACCEPT": Capabilities |= ImapCapabilities.UTF8Accept; break; - case "UTF8=ONLY": Capabilities |= ImapCapabilities.UTF8Only; break; - case "LITERAL-": Capabilities |= ImapCapabilities.LiteralMinus; break; - case "APPENDLIMIT": Capabilities |= ImapCapabilities.AppendLimit; break; - case "UNAUTHENTICATE": Capabilities |= ImapCapabilities.Unauthenticate; break; - case "STATUS=SIZE": Capabilities |= ImapCapabilities.StatusSize; break; - case "LIST-MYRIGHTS": Capabilities |= ImapCapabilities.ListMyRights; break; - case "OBJECTID": Capabilities |= ImapCapabilities.ObjectID; break; - case "REPLACE": Capabilities |= ImapCapabilities.Replace; break; - case "XLIST": Capabilities |= ImapCapabilities.XList; break; - case "X-GM-EXT-1": Capabilities |= ImapCapabilities.GMailExt1; QuirksMode = ImapQuirksMode.GMail; break; - case "XSTOP": QuirksMode = ImapQuirksMode.ProtonMail; break; - case "X-SUN-IMAP": QuirksMode = ImapQuirksMode.SunMicrosystems; break; - case "XYMHIGHESTMODSEQ": QuirksMode = ImapQuirksMode.Yahoo; break; - } - } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - AssertToken (token, sentinel, GenericItemSyntaxErrorFormat, "CAPABILITIES", token); - - // unget the sentinel - Stream.UngetToken (token); - - if ((Capabilities & ImapCapabilities.IMAP4rev1) != 0) { - ProtocolVersion = ImapProtocolVersion.IMAP4rev1; - Capabilities |= ImapCapabilities.Status; - } else if ((Capabilities & ImapCapabilities.IMAP4) != 0) { - ProtocolVersion = ImapProtocolVersion.IMAP4; - } - - if ((Capabilities & ImapCapabilities.QuickResync) != 0) - Capabilities |= ImapCapabilities.CondStore; - - if ((Capabilities & ImapCapabilities.UTF8Only) != 0) - Capabilities |= ImapCapabilities.UTF8Accept; - } - - async Task UpdateNamespacesAsync (bool doAsync, CancellationToken cancellationToken) - { - var namespaces = new List { - PersonalNamespaces, OtherNamespaces, SharedNamespaces - }; - ImapFolder folder; - ImapToken token; - string path; - char delim; - int n = 0; - - PersonalNamespaces.Clear (); - SharedNamespaces.Clear (); - OtherNamespaces.Clear (); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - do { - if (token.Type == ImapTokenType.OpenParen) { - // parse the list of namespace pairs... - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - while (token.Type == ImapTokenType.OpenParen) { - // parse the namespace pair - first token is the path - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); - - path = (string) token.Value; - - // second token is the directory separator - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.QString, ImapTokenType.Nil, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); - - var qstring = token.Type == ImapTokenType.Nil ? string.Empty : (string) token.Value; - - if (qstring.Length > 0) { - delim = qstring[0]; - - // canonicalize the namespace path - path = path.TrimEnd (delim); - } else { - delim = '\0'; - } - - namespaces[n].Add (new FolderNamespace (delim, DecodeMailboxName (path))); - - if (!GetCachedFolder (path, out folder)) { - folder = CreateImapFolder (path, FolderAttributes.None, delim); - CacheFolder (folder); - } - - folder.UpdateIsNamespace (true); - - do { - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - // NAMESPACE extension - - AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.OpenParen, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); - - do { - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); - } while (true); - } while (true); - - // read the next token - it should either be '(' or ')' - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - AssertToken (token, ImapTokenType.CloseParen, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); - } else { - AssertToken (token, ImapTokenType.Nil, GenericUntaggedResponseSyntaxErrorFormat, "NAMESPACE", token); - } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - n++; - } while (n < 3); - - while (token.Type != ImapTokenType.Eoln) - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - void ProcessResponseCodes (ImapCommand ic) - { - foreach (var code in ic.RespCodes) { - switch (code.Type) { - case ImapResponseCodeType.Alert: - OnAlert (code.Message); - break; - case ImapResponseCodeType.NotificationOverflow: - OnNotificationOverflow (); - break; - } - } - } - - void EmitMetadataChanged (Metadata metadata) - { - var encodedName = metadata.EncodedName; - ImapFolder folder; - - if (encodedName.Length == 0) { - OnMetadataChanged (metadata); - } else if (FolderCache.TryGetValue (encodedName, out folder)) { - folder.OnMetadataChanged (metadata); - } - } - - internal MetadataCollection FilterMetadata (MetadataCollection metadata, string encodedName) - { - for (int i = 0; i < metadata.Count; i++) { - if (metadata[i].EncodedName == encodedName) - continue; - - EmitMetadataChanged (metadata[i]); - metadata.RemoveAt (i); - i--; - } - - return metadata; - } - - internal void ProcessMetadataChanges (MetadataCollection metadata) - { - for (int i = 0; i < metadata.Count; i++) - EmitMetadataChanged (metadata[i]); - } - - internal static ImapResponseCodeType GetResponseCodeType (string atom) - { - switch (atom.ToUpperInvariant ()) { - case "ALERT": return ImapResponseCodeType.Alert; - case "BADCHARSET": return ImapResponseCodeType.BadCharset; - case "CAPABILITY": return ImapResponseCodeType.Capability; - case "NEWNAME": return ImapResponseCodeType.NewName; - case "PARSE": return ImapResponseCodeType.Parse; - case "PERMANENTFLAGS": return ImapResponseCodeType.PermanentFlags; - case "READ-ONLY": return ImapResponseCodeType.ReadOnly; - case "READ-WRITE": return ImapResponseCodeType.ReadWrite; - case "TRYCREATE": return ImapResponseCodeType.TryCreate; - case "UIDNEXT": return ImapResponseCodeType.UidNext; - case "UIDVALIDITY": return ImapResponseCodeType.UidValidity; - case "UNSEEN": return ImapResponseCodeType.Unseen; - case "REFERRAL": return ImapResponseCodeType.Referral; - case "UNKNOWN-CTE": return ImapResponseCodeType.UnknownCte; - case "APPENDUID": return ImapResponseCodeType.AppendUid; - case "COPYUID": return ImapResponseCodeType.CopyUid; - case "UIDNOTSTICKY": return ImapResponseCodeType.UidNotSticky; - case "URLMECH": return ImapResponseCodeType.UrlMech; - case "BADURL": return ImapResponseCodeType.BadUrl; - case "TOOBIG": return ImapResponseCodeType.TooBig; - case "HIGHESTMODSEQ": return ImapResponseCodeType.HighestModSeq; - case "MODIFIED": return ImapResponseCodeType.Modified; - case "NOMODSEQ": return ImapResponseCodeType.NoModSeq; - case "COMPRESSIONACTIVE": return ImapResponseCodeType.CompressionActive; - case "CLOSED": return ImapResponseCodeType.Closed; - case "NOTSAVED": return ImapResponseCodeType.NotSaved; - case "BADCOMPARATOR": return ImapResponseCodeType.BadComparator; - case "ANNOTATE": return ImapResponseCodeType.Annotate; - case "ANNOTATIONS": return ImapResponseCodeType.Annotations; - case "MAXCONVERTMESSAGES": return ImapResponseCodeType.MaxConvertMessages; - case "MAXCONVERTPARTS": return ImapResponseCodeType.MaxConvertParts; - case "TEMPFAIL": return ImapResponseCodeType.TempFail; - case "NOUPDATE": return ImapResponseCodeType.NoUpdate; - case "METADATA": return ImapResponseCodeType.Metadata; - case "NOTIFICATIONOVERFLOW": return ImapResponseCodeType.NotificationOverflow; - case "BADEVENT": return ImapResponseCodeType.BadEvent; - case "UNDEFINED-FILTER": return ImapResponseCodeType.UndefinedFilter; - case "UNAVAILABLE": return ImapResponseCodeType.Unavailable; - case "AUTHENTICATIONFAILED": return ImapResponseCodeType.AuthenticationFailed; - case "AUTHORIZATIONFAILED": return ImapResponseCodeType.AuthorizationFailed; - case "EXPIRED": return ImapResponseCodeType.Expired; - case "PRIVACYREQUIRED": return ImapResponseCodeType.PrivacyRequired; - case "CONTACTADMIN": return ImapResponseCodeType.ContactAdmin; - case "NOPERM": return ImapResponseCodeType.NoPerm; - case "INUSE": return ImapResponseCodeType.InUse; - case "EXPUNGEISSUED": return ImapResponseCodeType.ExpungeIssued; - case "CORRUPTION": return ImapResponseCodeType.Corruption; - case "SERVERBUG": return ImapResponseCodeType.ServerBug; - case "CLIENTBUG": return ImapResponseCodeType.ClientBug; - case "CANNOT": return ImapResponseCodeType.CanNot; - case "LIMIT": return ImapResponseCodeType.Limit; - case "OVERQUOTA": return ImapResponseCodeType.OverQuota; - case "ALREADYEXISTS": return ImapResponseCodeType.AlreadyExists; - case "NONEXISTENT": return ImapResponseCodeType.NonExistent; - case "USEATTR": return ImapResponseCodeType.UseAttr; - case "MAILBOXID": return ImapResponseCodeType.MailboxId; - default: return ImapResponseCodeType.Unknown; - } - } - - /// - /// Parses the response code. - /// - /// The response code. - /// Whether or not the resp-code is tagged vs untagged. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public async Task ParseResponseCodeAsync (bool isTagged, bool doAsync, CancellationToken cancellationToken) - { - uint validity = Selected != null ? Selected.UidValidity : 0; - ImapResponseCode code; - ImapToken token; - string atom; - -// token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); -// -// if (token.Type != ImapTokenType.LeftBracket) { -// Debug.WriteLine ("Expected a '[' followed by a RESP-CODE, but got: {0}", token); -// throw UnexpectedToken (token, false); -// } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Atom, "Syntax error in response code. Unexpected token: {0}", token); - - atom = (string) token.Value; - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - code = ImapResponseCode.Create (GetResponseCodeType (atom)); - code.IsTagged = isTagged; - - switch (code.Type) { - case ImapResponseCodeType.BadCharset: - if (token.Type == ImapTokenType.OpenParen) { - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - SupportedCharsets.Clear (); - while (token.Type == ImapTokenType.Atom || token.Type == ImapTokenType.QString) { - SupportedCharsets.Add ((string) token.Value); - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - AssertToken (token, ImapTokenType.CloseParen, GenericResponseCodeSyntaxErrorFormat, "BADCHARSET", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - break; - case ImapResponseCodeType.Capability: - Stream.UngetToken (token); - await UpdateCapabilitiesAsync (ImapTokenType.CloseBracket, doAsync, cancellationToken).ConfigureAwait (false); - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.PermanentFlags: - var perm = (PermanentFlagsResponseCode) code; - - Stream.UngetToken (token); - perm.Flags = await ImapUtils.ParseFlagsListAsync (this, "PERMANENTFLAGS", null, doAsync, cancellationToken).ConfigureAwait (false); - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.UidNext: - var next = (UidNextResponseCode) code; - - // Note: we allow '0' here because some servers have been known to send "* OK [UIDNEXT 0]". - // The *probable* explanation here is that the folder has never been opened and/or no messages - // have ever been delivered (yet) to that mailbox and so the UIDNEXT has not (yet) been - // initialized. - // - // See https://github.com/jstedfast/MailKit/issues/1010 for an example. - var uid = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, "UIDNEXT", token); - next.Uid = uid > 0 ? new UniqueId (uid) : UniqueId.Invalid; - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.UidValidity: - var uidvalidity = (UidValidityResponseCode) code; - - // Note: we allow '0' here because some servers have been known to send "* OK [UIDVALIDITY 0]". - // The *probable* explanation here is that the folder has never been opened and/or no messages - // have ever been delivered (yet) to that mailbox and so the UIDVALIDITY has not (yet) been - // initialized. - // - // See https://github.com/jstedfast/MailKit/issues/150 for an example. - uidvalidity.UidValidity = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, "UIDVALIDITY", token); - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.Unseen: - var unseen = (UnseenResponseCode) code; - - // Note: we allow '0' here because some servers have been known to send "* OK [UNSEEN 0]" when the - // mailbox contains no messages. - // - // See https://github.com/jstedfast/MailKit/issues/34 for details. - var n = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, "UNSEEN", token); - - unseen.Index = n > 0 ? (int) (n - 1) : 0; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.NewName: - var rename = (NewNameResponseCode) code; - - // Note: this RESP-CODE existed in rfc2060 but has been removed in rfc3501: - // - // 85) Remove NEWNAME. It can't work because mailbox names can be - // literals and can include "]". Functionality can be addressed via - // referrals. - AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericResponseCodeSyntaxErrorFormat, "NEWNAME", token); - - rename.OldName = (string) token.Value; - - // the next token should be another atom or qstring token representing the new name of the folder - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericResponseCodeSyntaxErrorFormat, "NEWNAME", token); - - rename.NewName = (string) token.Value; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.AppendUid: - var append = (AppendUidResponseCode) code; - - append.UidValidity = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, "APPENDUID", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - // The MULTIAPPEND extension redefines APPENDUID's second argument to be a uid-set instead of a single uid. - append.UidSet = ParseUidSet (token, append.UidValidity, GenericResponseCodeSyntaxErrorFormat, "APPENDUID", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.CopyUid: - var copy = (CopyUidResponseCode) code; - - copy.UidValidity = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - // Note: Outlook.com will apparently sometimes issue a [COPYUID nz_number SPACE SPACE] resp-code - // in response to a UID COPY or UID MOVE command. Likely this happens only when the source message - // didn't exist or something? See https://github.com/jstedfast/MailKit/issues/555 for details. - - if (token.Type != ImapTokenType.CloseBracket) { - copy.SrcUidSet = ParseUidSet (token, validity, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); - } else { - copy.SrcUidSet = new UniqueIdSet (); - Stream.UngetToken (token); - } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type != ImapTokenType.CloseBracket) { - copy.DestUidSet = ParseUidSet (token, copy.UidValidity, GenericResponseCodeSyntaxErrorFormat, "COPYUID", token); - } else { - copy.DestUidSet = new UniqueIdSet (); - Stream.UngetToken (token); - } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.BadUrl: - var badurl = (BadUrlResponseCode) code; - - AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericResponseCodeSyntaxErrorFormat, "BADURL", token); - - badurl.BadUrl = (string) token.Value; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.HighestModSeq: - var highest = (HighestModSeqResponseCode) code; - - highest.HighestModSeq = ParseNumber64 (token, false, GenericResponseCodeSyntaxErrorFormat, "HIGHESTMODSEQ", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.Modified: - var modified = (ModifiedResponseCode) code; - - modified.UidSet = ParseUidSet (token, validity, GenericResponseCodeSyntaxErrorFormat, "MODIFIED", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.MaxConvertMessages: - case ImapResponseCodeType.MaxConvertParts: - var maxConvert = (MaxConvertResponseCode) code; - - maxConvert.MaxConvert = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, atom, token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.NoUpdate: - var noUpdate = (NoUpdateResponseCode) code; - - AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, GenericResponseCodeSyntaxErrorFormat, "NOUPDATE", token); - - noUpdate.Tag = (string) token.Value; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.Annotate: - var annotate = (AnnotateResponseCode) code; - - AssertToken (token, ImapTokenType.Atom, GenericResponseCodeSyntaxErrorFormat, "ANNOTATE", token); - - switch (((string) token.Value).ToUpperInvariant ()) { - case "TOOBIG": - annotate.SubType = AnnotateResponseCodeSubType.TooBig; - break; - case "TOOMANY": - annotate.SubType = AnnotateResponseCodeSubType.TooMany; - break; - } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.Annotations: - var annotations = (AnnotationsResponseCode) code; - - AssertToken (token, ImapTokenType.Atom, GenericResponseCodeSyntaxErrorFormat, "ANNOTATIONS", token); - - switch (((string) token.Value).ToUpperInvariant ()) { - case "NONE": break; - case "READ-ONLY": - annotations.Access = AnnotationAccess.ReadOnly; - break; - default: - annotations.Access = AnnotationAccess.ReadWrite; - annotations.MaxSize = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, "ANNOTATIONS", token); - break; - } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (annotations.Access != AnnotationAccess.None) { - annotations.Scopes = AnnotationScope.Both; - - if (token.Type != ImapTokenType.CloseBracket) { - AssertToken (token, ImapTokenType.Atom, GenericResponseCodeSyntaxErrorFormat, "ANNOTATIONS", token); - - if (((string) token.Value).Equals ("NOPRIVATE", StringComparison.OrdinalIgnoreCase)) - annotations.Scopes = AnnotationScope.Shared; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - } - - break; - case ImapResponseCodeType.Metadata: - var metadata = (MetadataResponseCode) code; - - AssertToken (token, ImapTokenType.Atom, GenericResponseCodeSyntaxErrorFormat, "METADATA", token); - - switch (((string) token.Value).ToUpperInvariant ()) { - case "LONGENTRIES": - metadata.SubType = MetadataResponseCodeSubType.LongEntries; - metadata.IsError = false; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - metadata.Value = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, "METADATA LONGENTRIES", token); - break; - case "MAXSIZE": - metadata.SubType = MetadataResponseCodeSubType.MaxSize; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - metadata.Value = ParseNumber (token, false, GenericResponseCodeSyntaxErrorFormat, "METADATA MAXSIZE", token); - break; - case "TOOMANY": - metadata.SubType = MetadataResponseCodeSubType.TooMany; - break; - case "NOPRIVATE": - metadata.SubType = MetadataResponseCodeSubType.NoPrivate; - break; - } - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.UndefinedFilter: - var undefined = (UndefinedFilterResponseCode) code; - - AssertToken (token, ImapTokenType.Atom, GenericResponseCodeSyntaxErrorFormat, "UNDEFINED-FILTER", token); - - undefined.Name = (string) token.Value; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapResponseCodeType.MailboxId: - var mailboxid = (MailboxIdResponseCode) code; - - AssertToken (token, ImapTokenType.OpenParen, GenericResponseCodeSyntaxErrorFormat, "MAILBOXID", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Atom, GenericResponseCodeSyntaxErrorFormat, "MAILBOXID", token); - - mailboxid.MailboxId = (string) token.Value; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.CloseParen, GenericResponseCodeSyntaxErrorFormat, "MAILBOXID", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - default: - // Note: This code-path handles: [ALERT], [CLOSED], [READ-ONLY], [READ-WRITE], etc. - - //if (code.Type == ImapResponseCodeType.Unknown) - // Debug.WriteLine (string.Format ("Unknown RESP-CODE encountered: {0}", atom)); - - // extensions are of the form: "[" atom [SPACE 1*] "]" - - // skip over tokens until we get to a ']' - while (token.Type != ImapTokenType.CloseBracket && token.Type != ImapTokenType.Eoln) - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - break; - } - - AssertToken (token, ImapTokenType.CloseBracket, "Syntax error in response code. Unexpected token: {0}", token); - - code.Message = (await ReadLineAsync (doAsync, cancellationToken).ConfigureAwait (false)).Trim (); - - return code; - } - - async Task UpdateStatusAsync (bool doAsync, CancellationToken cancellationToken) - { - var token = await ReadTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - ImapFolder folder; - uint count, uid; - ulong modseq; - string name; - - switch (token.Type) { - case ImapTokenType.Literal: - name = await ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - name = (string) token.Value; - break; - case ImapTokenType.Nil: - // Note: according to rfc3501, section 4.5, NIL is acceptable as a mailbox name. - name = "NIL"; - break; - default: - throw UnexpectedToken (GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - } - - // Note: if the folder is null, then it probably means the user is using NOTIFY - // and hasn't yet requested the folder. That's ok. - GetCachedFolder (name, out folder); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.OpenParen, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - do { - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - var atom = (string) token.Value; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - switch (atom.ToUpperInvariant ()) { - case "HIGHESTMODSEQ": - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - modseq = ParseNumber64 (token, false, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.UpdateHighestModSeq (modseq); - break; - case "MESSAGES": - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - count = ParseNumber (token, false, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.OnExists ((int) count); - break; - case "RECENT": - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - count = ParseNumber (token, false, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.OnRecent ((int) count); - break; - case "UIDNEXT": - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - uid = ParseNumber (token, false, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.UpdateUidNext (uid > 0 ? new UniqueId (uid) : UniqueId.Invalid); - break; - case "UIDVALIDITY": - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - uid = ParseNumber (token, false, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.UpdateUidValidity (uid); - break; - case "UNSEEN": - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - count = ParseNumber (token, false, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.UpdateUnread ((int) count); - break; - case "APPENDLIMIT": - if (token.Type == ImapTokenType.Atom) { - var limit = ParseNumber (token, false, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.UpdateAppendLimit (limit); - } else { - AssertToken (token, ImapTokenType.Nil, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - if (folder != null) - folder.UpdateAppendLimit (null); - } - break; - case "SIZE": - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - var size = ParseNumber64 (token, false, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.UpdateSize (size); - break; - case "MAILBOXID": - AssertToken (token, ImapTokenType.OpenParen, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Atom, GenericItemSyntaxErrorFormat, atom, token); - - if (folder != null) - folder.UpdateId ((string) token.Value); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.CloseParen, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - break; - } - } while (true); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Eoln, GenericUntaggedResponseSyntaxErrorFormat, "STATUS", token); - } - - /// - /// Processes an untagged response. - /// - /// The untagged response. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - internal async Task ProcessUntaggedResponseAsync (bool doAsync, CancellationToken cancellationToken) - { - var token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - var folder = current.Folder ?? Selected; - var result = ImapUntaggedResult.Handled; - ImapUntaggedHandler handler; - uint number; - 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); - atom = "OK"; - } else if (token.Type != ImapTokenType.Atom) { - // if we get anything else here, just ignore it? - Stream.UngetToken (token); - await SkipLineAsync (doAsync, cancellationToken).ConfigureAwait (false); - return result; - } else { - atom = (string) token.Value; - } - - switch (atom.ToUpperInvariant ()) { - case "BYE": - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenBracket) { - var code = await ParseResponseCodeAsync (false, doAsync, cancellationToken).ConfigureAwait (false); - current.RespCodes.Add (code); - } else { - var text = token.Value.ToString () + await ReadLineAsync (doAsync, cancellationToken).ConfigureAwait (false); - current.ResponseText = text.TrimEnd (); - } - - current.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 - // the very first untagged BYE response and do not hold out hoping for a tagged response following the - // untagged BYE. - // - // See https://github.com/jstedfast/MailKit/issues/938 for details. - if (QuirksMode == ImapQuirksMode.Yandex && !current.Logout) - current.Status = ImapCommandStatus.Complete; - break; - case "CAPABILITY": - await UpdateCapabilitiesAsync (ImapTokenType.Eoln, doAsync, cancellationToken); - - // read the eoln token - await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case "ENABLED": - do { - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Eoln) - break; - - AssertToken (token, ImapTokenType.Atom, GenericUntaggedResponseSyntaxErrorFormat, atom, token); - - var feature = (string) token.Value; - switch (feature.ToUpperInvariant ()) { - case "UTF8=ACCEPT": UTF8Enabled = true; break; - case "QRESYNC": QResyncEnabled = true; break; - } - } while (true); - break; - case "FLAGS": - folder.UpdateAcceptedFlags (await ImapUtils.ParseFlagsListAsync (this, atom, null, doAsync, cancellationToken).ConfigureAwait (false)); - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Eoln, GenericUntaggedResponseSyntaxErrorFormat, atom, token); - break; - case "NAMESPACE": - await UpdateNamespacesAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case "STATUS": - await UpdateStatusAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case "OK": case "NO": case "BAD": - if (atom.Equals ("OK", StringComparison.OrdinalIgnoreCase)) - result = ImapUntaggedResult.Ok; - else if (atom.Equals ("NO", StringComparison.OrdinalIgnoreCase)) - result = ImapUntaggedResult.No; - else - result = ImapUntaggedResult.Bad; - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenBracket) { - var code = await ParseResponseCodeAsync (false, doAsync, cancellationToken).ConfigureAwait (false); - current.RespCodes.Add (code); - } else if (token.Type != ImapTokenType.Eoln) { - var text = ((string) token.Value) + await ReadLineAsync (doAsync, cancellationToken).ConfigureAwait (false); - current.ResponseText = text.TrimEnd (); - } - break; - default: - if (uint.TryParse (atom, NumberStyles.None, CultureInfo.InvariantCulture, out number)) { - // we probably have something like "* 1 EXISTS" - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - AssertToken (token, ImapTokenType.Atom, "Syntax error in untagged response. Unexpected token: {0}", token); - - atom = (string) token.Value; - - if (current.UntaggedHandlers.TryGetValue (atom, out handler)) { - // the command registered an untagged handler for this atom... - await handler (this, current, (int) number - 1, doAsync).ConfigureAwait (false); - } else if (folder != null) { - switch (atom.ToUpperInvariant ()) { - case "EXISTS": - folder.OnExists ((int) number); - break; - case "EXPUNGE": - if (number == 0) - throw UnexpectedToken ("Syntax error in untagged EXPUNGE response. Unexpected message index: 0"); - - folder.OnExpunge ((int) number - 1); - break; - case "FETCH": - // Apparently Courier-IMAP (2004) will reply with "* 0 FETCH ..." sometimes. - // See https://github.com/jstedfast/MailKit/issues/428 for details. - //if (number == 0) - // throw UnexpectedToken ("Syntax error in untagged FETCH response. Unexpected message index: 0"); - - await folder.OnFetchAsync (this, (int) number - 1, doAsync, cancellationToken).ConfigureAwait (false); - break; - case "RECENT": - folder.OnRecent ((int) number); - break; - default: - //Debug.WriteLine ("Unhandled untagged response: * {0} {1}", number, atom); - break; - } - } else { - //Debug.WriteLine ("Unhandled untagged response: * {0} {1}", number, atom); - } - - await SkipLineAsync (doAsync, cancellationToken).ConfigureAwait (false); - } else if (current.UntaggedHandlers.TryGetValue (atom, out handler)) { - // the command registered an untagged handler for this atom... - await handler (this, current, -1, doAsync).ConfigureAwait (false); - await SkipLineAsync (doAsync, cancellationToken).ConfigureAwait (false); - } else if (atom.Equals ("LIST", StringComparison.OrdinalIgnoreCase)) { - // unsolicited LIST response - probably due to NOTIFY MailboxName or MailboxSubscribe event - await ImapUtils.ParseFolderListAsync (this, null, false, true, doAsync, cancellationToken).ConfigureAwait (false); - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - AssertToken (token, ImapTokenType.Eoln, "Syntax error in untagged LIST response. Unexpected token: {0}", token); - } else if (atom.Equals ("METADATA", StringComparison.OrdinalIgnoreCase)) { - // unsolicited METADATA response - probably due to NOTIFY MailboxMetadataChange or ServerMetadataChange - var metadata = new MetadataCollection (); - await ImapUtils.ParseMetadataAsync (this, metadata, doAsync, cancellationToken).ConfigureAwait (false); - ProcessMetadataChanges (metadata); - - token = await ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - AssertToken (token, ImapTokenType.Eoln, "Syntax error in untagged LIST response. Unexpected token: {0}", token); - } else if (atom.Equals ("VANISHED", StringComparison.OrdinalIgnoreCase) && folder != null) { - await folder.OnVanishedAsync (this, doAsync, cancellationToken).ConfigureAwait (false); - await SkipLineAsync (doAsync, cancellationToken).ConfigureAwait (false); - } else { - // don't know how to handle this... eat it? - await SkipLineAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - break; - } - - return result; - } - - /// - /// Iterate the command pipeline. - /// - async Task IterateAsync (bool doAsync) - { - lock (queue) { - if (queue.Count == 0) - throw new InvalidOperationException ("The IMAP command queue is empty."); - - if (IsBusy) - throw new InvalidOperationException ("The ImapClient is currently busy processing a command in another thread. Lock the SyncRoot property to properly synchronize your threads."); - - current = queue[0]; - queue.RemoveAt (0); - - try { - current.CancellationToken.ThrowIfCancellationRequested (); - } catch { - queue.RemoveAll (x => x.CancellationToken.IsCancellationRequested); - current = null; - throw; - } - } - - current.Status = ImapCommandStatus.Active; - - try { - while (await current.StepAsync (doAsync).ConfigureAwait (false)) { - // more literal data to send... - } - - if (current.Bye && !current.Logout) - throw new ImapProtocolException ("Bye."); - } catch (ImapProtocolException) { - var ic = current; - - Disconnect (); - - if (ic.Bye) { - if (ic.RespCodes.Count > 0) { - var code = ic.RespCodes[ic.RespCodes.Count - 1]; - - if (code.Type == ImapResponseCodeType.Alert) { - OnAlert (code.Message); - - throw new ImapProtocolException (code.Message); - } - } - - if (!string.IsNullOrEmpty (ic.ResponseText)) - throw new ImapProtocolException (ic.ResponseText); - } - - throw; - } catch { - Disconnect (); - throw; - } finally { - current = null; - } - } - - /// - /// Wait for the specified command to finish. - /// - /// The IMAP command. - /// Whether or not asynchronous IO methods should be used. - /// - /// is null. - /// - public async Task RunAsync (ImapCommand ic, bool doAsync) - { - if (ic == null) - throw new ArgumentNullException (nameof (ic)); - - while (ic.Status < ImapCommandStatus.Complete) { - // continue processing commands... - await IterateAsync (doAsync).ConfigureAwait (false); - } - - ProcessResponseCodes (ic); - } - - public IEnumerable CreateCommands (CancellationToken cancellationToken, ImapFolder folder, string format, IList uids, params object[] args) - { - var vargs = new List (); - int maxLength; - - // we assume that uids is the first formatter (with a %s) - vargs.Add ("1"); - - for (int i = 0; i < args.Length; i++) - vargs.Add (args[i]); - - args = vargs.ToArray (); - - if (QuirksMode == ImapQuirksMode.Courier) { - // Courier IMAP's command parser allows each token to be up to 16k in size. - maxLength = 16 * 1024; - } else { - int estimated = ImapCommand.EstimateCommandLength (this, format, args); - - switch (QuirksMode) { - case ImapQuirksMode.Dovecot: - // Dovecot, by default, allows commands up to 64k. - // See https://github.com/dovecot/core/blob/master/src/imap/imap-settings.c#L94 - maxLength = Math.Max ((64 * 1042) - estimated, 24); - break; - case ImapQuirksMode.GMail: - // GMail seems to support command-lines up to at least 16k. - maxLength = Math.Max ((16 * 1042) - estimated, 24); - break; - case ImapQuirksMode.Yahoo: - case ImapQuirksMode.UW: - // Follow the IMAP4 Implementation Recommendations which states that clients - // *SHOULD* limit their command lengths to 1000 octets. - maxLength = Math.Max (1000 - estimated, 24); - break; - default: - // Push the boundaries of the IMAP4 Implementation Recommendations which states - // that servers *SHOULD* accept command lengths of up to 8000 octets. - maxLength = Math.Max (8000 - estimated, 24); - break; - } - } - - foreach (var subset in UniqueIdSet.EnumerateSerializedSubsets (uids, maxLength)) { - args[0] = subset; - - yield return new ImapCommand (this, cancellationToken, folder, format, args); - } - } - - public IEnumerable QueueCommands (CancellationToken cancellationToken, ImapFolder folder, string format, IList uids, params object[] args) - { - foreach (var ic in CreateCommands (cancellationToken, folder, format, uids, args)) { - QueueCommand (ic); - yield return ic; - } - } - - /// - /// Queues the command. - /// - /// The command. - /// The cancellation token. - /// The folder that the command operates on. - /// The formatting options. - /// The command format. - /// The command arguments. - 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); - return ic; - } - - /// - /// Queues the command. - /// - /// The command. - /// The cancellation token. - /// 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) - { - return QueueCommand (cancellationToken, folder, FormatOptions.Default, format, args); - } - - /// - /// Queues the command. - /// - /// The IMAP command. - public void QueueCommand (ImapCommand ic) - { - lock (queue) { - ic.Status = ImapCommandStatus.Queued; - queue.Add (ic); - } - } - - /// - /// Queries the capabilities. - /// - /// The command result. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public async Task QueryCapabilitiesAsync (bool doAsync, CancellationToken cancellationToken) - { - var ic = QueueCommand (cancellationToken, null, "CAPABILITY\r\n"); - - await RunAsync (ic, doAsync).ConfigureAwait (false); - - return ic.Response; - } - - /// - /// Cache the specified folder. - /// - /// The folder. - public void CacheFolder (ImapFolder folder) - { - if ((folder.Attributes & FolderAttributes.Inbox) != 0) - cacheComparer.DirectorySeparator = folder.DirectorySeparator; - - FolderCache.Add (folder.EncodedName, folder); - } - - /// - /// Gets the cached folder. - /// - /// true if the folder was retreived from the cache; otherwise, false. - /// The encoded folder name. - /// The cached folder. - public bool GetCachedFolder (string encodedName, out ImapFolder folder) - { - return FolderCache.TryGetValue (encodedName, out folder); - } - - /// - /// Looks up and sets the property of each of the folders. - /// - /// The IMAP folders. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - internal async Task LookupParentFoldersAsync (IEnumerable folders, bool doAsync, CancellationToken cancellationToken) - { - var list = new List (folders); - string encodedName, pattern; - ImapFolder parent; - int index; - - // Note: we use a for-loop instead of foreach because we conditionally add items to the list. - for (int i = 0; i < list.Count; i++) { - var folder = list[i]; - - if (folder.ParentFolder != null) - continue; - - // FIXME: should this search EncodedName instead of FullName? - if ((index = folder.FullName.LastIndexOf (folder.DirectorySeparator)) != -1) { - if (index == 0) - continue; - - var parentName = folder.FullName.Substring (0, index); - encodedName = EncodeMailboxName (parentName); - } else { - encodedName = string.Empty; - } - - if (GetCachedFolder (encodedName, out parent)) { - folder.ParentFolder = parent; - continue; - } - - // Note: folder names can contain wildcards (including '*' and '%'), so replace '*' with '%' - // in order to reduce the list of folders returned by our LIST command. - pattern = encodedName.Replace ('*', '%'); - - var command = new StringBuilder ("LIST \"\" %S"); - var returnsSubscribed = false; - - if ((Capabilities & ImapCapabilities.ListExtended) != 0) { - // Try to get the \Subscribed and \HasChildren or \HasNoChildren attributes - command.Append (" RETURN (SUBSCRIBED CHILDREN)"); - returnsSubscribed = true; - } - - command.Append ("\r\n"); - - var ic = new ImapCommand (this, cancellationToken, null, command.ToString (), pattern); - ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderListAsync); - ic.ListReturnsSubscribed = returnsSubscribed; - ic.UserData = new List (); - - QueueCommand (ic); - - await RunAsync (ic, doAsync).ConfigureAwait (false); - - if (!GetCachedFolder (encodedName, out parent)) { - parent = CreateImapFolder (encodedName, FolderAttributes.NonExistent, folder.DirectorySeparator); - CacheFolder (parent); - } else if (parent.ParentFolder == null && !parent.IsNamespace) { - list.Add (parent); - } - - folder.ParentFolder = parent; - } - } - - /// - /// Queries the namespaces. - /// - /// The command result. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public async Task QueryNamespacesAsync (bool doAsync, CancellationToken cancellationToken) - { - ImapCommand ic; - - if ((Capabilities & ImapCapabilities.Namespace) != 0) { - ic = QueueCommand (cancellationToken, null, "NAMESPACE\r\n"); - await RunAsync (ic, doAsync).ConfigureAwait (false); - } else { - var list = new List (); - - ic = new ImapCommand (this, cancellationToken, null, "LIST \"\" \"\"\r\n"); - ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderListAsync); - ic.UserData = list; - - QueueCommand (ic); - await RunAsync (ic, doAsync).ConfigureAwait (false); - - PersonalNamespaces.Clear (); - SharedNamespaces.Clear (); - OtherNamespaces.Clear (); - - if (list.Count > 0) { - var empty = list.FirstOrDefault (x => x.EncodedName.Length == 0); - - if (empty == null) { - empty = CreateImapFolder (string.Empty, FolderAttributes.None, list[0].DirectorySeparator); - CacheFolder (empty); - } - - PersonalNamespaces.Add (new FolderNamespace (empty.DirectorySeparator, empty.FullName)); - empty.UpdateIsNamespace (true); - } - - await LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false); - } - - return ic.Response; - } - - internal static ImapFolder GetFolder (List folders, string encodedName) - { - for (int i = 0; i < folders.Count; i++) { - if (encodedName.Equals (folders[i].EncodedName, StringComparison.OrdinalIgnoreCase)) - return folders[i]; - } - - return null; - } - - /// - /// Assigns a folder as a special folder. - /// - /// The special folder. - public void AssignSpecialFolder (ImapFolder folder) - { - if ((folder.Attributes & FolderAttributes.All) != 0) - All = folder; - if ((folder.Attributes & FolderAttributes.Archive) != 0) - Archive = folder; - if ((folder.Attributes & FolderAttributes.Drafts) != 0) - Drafts = folder; - if ((folder.Attributes & FolderAttributes.Flagged) != 0) - Flagged = folder; - if ((folder.Attributes & FolderAttributes.Important) != 0) - Important = folder; - if ((folder.Attributes & FolderAttributes.Junk) != 0) - Junk = folder; - if ((folder.Attributes & FolderAttributes.Sent) != 0) - Sent = folder; - if ((folder.Attributes & FolderAttributes.Trash) != 0) - Trash = folder; - } - - /// - /// Assigns the special folders. - /// - /// The list of folders. - public void AssignSpecialFolders (IList list) - { - for (int i = 0; i < list.Count; i++) - AssignSpecialFolder (list[i]); - } - - /// - /// Queries the special folders. - /// - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public async Task QuerySpecialFoldersAsync (bool doAsync, CancellationToken cancellationToken) - { - var command = new StringBuilder ("LIST \"\" \"INBOX\""); - var list = new List (); - var returnsSubscribed = false; - ImapFolder folder; - ImapCommand ic; - - if ((Capabilities & ImapCapabilities.ListExtended) != 0) { - command.Append (" RETURN (SUBSCRIBED CHILDREN)"); - returnsSubscribed = true; - } - - command.Append ("\r\n"); - - ic = new ImapCommand (this, cancellationToken, null, command.ToString ()); - ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderListAsync); - ic.ListReturnsSubscribed = returnsSubscribed; - ic.UserData = list; - - QueueCommand (ic); - - await RunAsync (ic, doAsync).ConfigureAwait (false); - - GetCachedFolder ("INBOX", out folder); - Inbox = folder; - - list.Clear (); - - if ((Capabilities & ImapCapabilities.SpecialUse) != 0) { - // Note: Some IMAP servers like ProtonMail respond to SPECIAL-USE LIST queries with BAD, so fall - // back to just issuing a standard LIST command and hope we get back some SPECIAL-USE attributes. - // - // See https://github.com/jstedfast/MailKit/issues/674 for dertails. - returnsSubscribed = false; - command.Clear (); - - command.Append ("LIST "); - - if (QuirksMode != ImapQuirksMode.ProtonMail) - command.Append ("(SPECIAL-USE) \"\" \"*\""); - else - command.Append ("\"\" \"%%\""); - - if ((Capabilities & ImapCapabilities.ListExtended) != 0) { - command.Append (" RETURN (SUBSCRIBED CHILDREN)"); - returnsSubscribed = true; - } - - command.Append ("\r\n"); - - ic = new ImapCommand (this, cancellationToken, null, command.ToString ()); - ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderListAsync); - ic.ListReturnsSubscribed = returnsSubscribed; - ic.UserData = list; - - QueueCommand (ic); - - await RunAsync (ic, doAsync).ConfigureAwait (false); - await LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false); - - AssignSpecialFolders (list); - } else if ((Capabilities & ImapCapabilities.XList) != 0) { - ic = new ImapCommand (this, cancellationToken, null, "XLIST \"\" \"*\"\r\n"); - ic.RegisterUntaggedHandler ("XLIST", ImapUtils.ParseFolderListAsync); - ic.UserData = list; - - QueueCommand (ic); - - await RunAsync (ic, doAsync).ConfigureAwait (false); - await LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false); - - AssignSpecialFolders (list); - } - } - - /// - /// Gets the folder representing the specified quota root. - /// - /// The folder. - /// The name of the quota root. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public async Task GetQuotaRootFolderAsync (string quotaRoot, bool doAsync, CancellationToken cancellationToken) - { - ImapFolder folder; - - if (GetCachedFolder (quotaRoot, out folder)) - return folder; - - var command = new StringBuilder ("LIST \"\" %S"); - var list = new List (); - var returnsSubscribed = false; - - if ((Capabilities & ImapCapabilities.ListExtended) != 0) { - command.Append (" RETURN (SUBSCRIBED CHILDREN)"); - returnsSubscribed = true; - } - - command.Append ("\r\n"); - - var ic = new ImapCommand (this, cancellationToken, null, command.ToString (), quotaRoot); - ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderListAsync); - ic.ListReturnsSubscribed = returnsSubscribed; - ic.UserData = list; - - QueueCommand (ic); - - await RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("LIST", ic); - - if ((folder = GetFolder (list, quotaRoot)) == null) { - folder = CreateImapFolder (quotaRoot, FolderAttributes.NonExistent, '.'); - CacheFolder (folder); - return folder; - } - - await LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false); - - return folder; - } - - /// - /// Gets the folder for the specified path. - /// - /// The folder. - /// The folder path. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public async Task GetFolderAsync (string path, bool doAsync, CancellationToken cancellationToken) - { - var encodedName = EncodeMailboxName (path); - ImapFolder folder; - - if (GetCachedFolder (encodedName, out folder)) - return folder; - - var command = new StringBuilder ("LIST \"\" %S"); - var list = new List (); - var returnsSubscribed = false; - - if ((Capabilities & ImapCapabilities.ListExtended) != 0) { - command.Append (" RETURN (SUBSCRIBED CHILDREN)"); - returnsSubscribed = true; - } - - command.Append ("\r\n"); - - var ic = new ImapCommand (this, cancellationToken, null, command.ToString (), encodedName); - ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderListAsync); - ic.ListReturnsSubscribed = returnsSubscribed; - ic.UserData = list; - - QueueCommand (ic); - - await RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("LIST", ic); - - if ((folder = GetFolder (list, encodedName)) == null) - throw new FolderNotFoundException (path); - - await LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false); - - return folder; - } - - internal string GetStatusQuery (StatusItems items) - { - var flags = string.Empty; - - if ((items & StatusItems.Count) != 0) - flags += "MESSAGES "; - if ((items & StatusItems.Recent) != 0) - flags += "RECENT "; - if ((items & StatusItems.UidNext) != 0) - flags += "UIDNEXT "; - if ((items & StatusItems.UidValidity) != 0) - flags += "UIDVALIDITY "; - if ((items & StatusItems.Unread) != 0) - flags += "UNSEEN "; - - if ((Capabilities & ImapCapabilities.CondStore) != 0) { - if ((items & StatusItems.HighestModSeq) != 0) - flags += "HIGHESTMODSEQ "; - } - - // Note: If the IMAP server specifies a limit in the CAPABILITY response, then - // it seems we cannot expect to be able to query this in a STATUS command... - if ((Capabilities & ImapCapabilities.AppendLimit) != 0 && !AppendLimit.HasValue) { - if ((items & StatusItems.AppendLimit) != 0) - flags += "APPENDLIMIT "; - } - - if ((Capabilities & ImapCapabilities.StatusSize) != 0) { - if ((items & StatusItems.Size) != 0) - flags += "SIZE "; - } - - if ((Capabilities & ImapCapabilities.ObjectID) != 0) { - if ((items & StatusItems.MailboxId) != 0) - flags += "MAILBOXID "; - } - - return flags.TrimEnd (); - } - - /// - /// Get all of the folders within the specified namespace. - /// - /// - /// Gets all of the folders within the specified namespace. - /// - /// The list of folders. - /// The namespace. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public async Task> GetFoldersAsync (FolderNamespace @namespace, StatusItems items, bool subscribedOnly, bool doAsync, CancellationToken cancellationToken) - { - var encodedName = EncodeMailboxName (@namespace.Path); - var pattern = encodedName.Length > 0 ? encodedName + @namespace.DirectorySeparator : string.Empty; - var status = items != StatusItems.None; - var list = new List (); - var command = new StringBuilder (); - var returnsSubscribed = false; - var lsub = subscribedOnly; - ImapFolder folder; - - if (!GetCachedFolder (encodedName, out folder)) - throw new FolderNotFoundException (@namespace.Path); - - if (subscribedOnly) { - if ((Capabilities & ImapCapabilities.ListExtended) != 0) { - command.Append ("LIST (SUBSCRIBED)"); - returnsSubscribed = true; - lsub = false; - } else { - command.Append ("LSUB"); - } - } else { - command.Append ("LIST"); - } - - command.Append (" \"\" %S"); - - if (!lsub) { - if (items != StatusItems.None && (Capabilities & ImapCapabilities.ListStatus) != 0) { - command.Append (" RETURN ("); - - if ((Capabilities & ImapCapabilities.ListExtended) != 0) { - if (!subscribedOnly) { - command.Append ("SUBSCRIBED "); - returnsSubscribed = true; - } - command.Append ("CHILDREN "); - } - - command.AppendFormat ("STATUS ({0})", GetStatusQuery (items)); - command.Append (')'); - status = false; - } else if ((Capabilities & ImapCapabilities.ListExtended) != 0) { - command.Append (" RETURN ("); - if (!subscribedOnly) { - command.Append ("SUBSCRIBED "); - returnsSubscribed = true; - } - command.Append ("CHILDREN"); - command.Append (')'); - } - } - - command.Append ("\r\n"); - - var ic = new ImapCommand (this, cancellationToken, null, command.ToString (), pattern + "*"); - ic.RegisterUntaggedHandler (lsub ? "LSUB" : "LIST", ImapUtils.ParseFolderListAsync); - ic.ListReturnsSubscribed = returnsSubscribed; - ic.UserData = list; - ic.Lsub = lsub; - - QueueCommand (ic); - - await RunAsync (ic, doAsync).ConfigureAwait (false); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create (lsub ? "LSUB" : "LIST", ic); - - await LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false); - - if (status) { - for (int i = 0; i < list.Count; i++) { - if (list[i].Exists) - await list[i].StatusAsync (items, doAsync, false, cancellationToken).ConfigureAwait (false); - } - } - - return list; - } - - /// - /// Decodes the name of the mailbox. - /// - /// The mailbox name. - /// The encoded name. - public string DecodeMailboxName (string encodedName) - { - return UTF8Enabled ? encodedName : ImapEncoding.Decode (encodedName); - } - - /// - /// Encodes the name of the mailbox. - /// - /// The mailbox name. - /// The encoded mailbox name. - public string EncodeMailboxName (string mailboxName) - { - return UTF8Enabled ? mailboxName : ImapEncoding.Encode (mailboxName); - } - - /// - /// Determines whether the mailbox name is valid or not. - /// - /// true if the mailbox name is valid; otherwise, false. - /// The mailbox name. - /// The path delimeter. - public bool IsValidMailboxName (string mailboxName, char delim) - { - // From rfc6855: - // - // Mailbox names MUST comply with the Net-Unicode Definition ([RFC5198], Section 2) - // with the specific exception that they MUST NOT contain control characters - // (U+0000-U+001F and U+0080-U+009F), a delete character (U+007F), a line separator (U+2028), - // or a paragraph separator (U+2029). - for (int i = 0; i < mailboxName.Length; i++) { - char c = mailboxName[i]; - - if (c <= 0x1F || (c >= 0x80 && c <= 0x9F) || c == 0x7F || c == 0x2028 || c == 0x2029 || c == delim) - return false; - } - - return mailboxName.Length > 0; - } - - void InitializeParser (Stream stream, bool persistent) - { - if (parser == null) - parser = new MimeParser (ParserOptions.Default, stream, persistent); - else - parser.SetStream (ParserOptions.Default, stream, persistent); - } - - public async Task ParseHeadersAsync (Stream stream, bool doAsync, CancellationToken cancellationToken) - { - InitializeParser (stream, false); - - if (doAsync) - return await parser.ParseHeadersAsync (cancellationToken).ConfigureAwait (false); - - return parser.ParseHeaders (cancellationToken); - } - - public async Task ParseMessageAsync (Stream stream, bool persistent, bool doAsync, CancellationToken cancellationToken) - { - InitializeParser (stream, persistent); - - if (doAsync) - return await parser.ParseMessageAsync (cancellationToken).ConfigureAwait (false); - - return parser.ParseMessage (cancellationToken); - } - - public async Task ParseEntityAsync (Stream stream, bool persistent, bool doAsync, CancellationToken cancellationToken) - { - InitializeParser (stream, persistent); - - if (doAsync) - return await parser.ParseEntityAsync (cancellationToken).ConfigureAwait (false); - - return parser.ParseEntity (cancellationToken); - } - - /// - /// Occurs when the engine receives an alert message from the server. - /// - public event EventHandler Alert; - - internal void OnAlert (string message) - { - var handler = Alert; - - if (handler != null) - handler (this, new AlertEventArgs (message)); - } - - /// - /// Occurs when the engine receives a notification that a folder has been created. - /// - public event EventHandler FolderCreated; - - internal void OnFolderCreated (IMailFolder folder) - { - var handler = FolderCreated; - - if (handler != null) - handler (this, new FolderCreatedEventArgs (folder)); - } - - /// - /// Occurs when the engine receives a notification that metadata has changed. - /// - public event EventHandler MetadataChanged; - - internal void OnMetadataChanged (Metadata metadata) - { - var handler = MetadataChanged; - - if (handler != null) - handler (this, new MetadataChangedEventArgs (metadata)); - } - - /// - /// Occurs when the engine receives a notification overflow message from the server. - /// - public event EventHandler NotificationOverflow; - - internal void OnNotificationOverflow () - { - // [NOTIFICATIONOVERFLOW] will reset to NOTIFY NONE - NotifySelectedNewExpunge = false; - - var handler = NotificationOverflow; - - if (handler != null) - handler (this, EventArgs.Empty); - } - - public event EventHandler Disconnected; - - void OnDisconnected () - { - var handler = Disconnected; - - if (handler != null) - handler (this, EventArgs.Empty); - } - - /// - /// Releases all resource used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. - public void Dispose () - { - disposed = true; - Disconnect (); - } - } -} diff --git a/src/MailKit/Net/Imap/ImapEventGroup.cs b/src/MailKit/Net/Imap/ImapEventGroup.cs deleted file mode 100644 index e60255e..0000000 --- a/src/MailKit/Net/Imap/ImapEventGroup.cs +++ /dev/null @@ -1,692 +0,0 @@ -// -// ImapFolderFetch.cs -// -// Authors: Steffen Kieß -// Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections.Generic; - -using MimeKit; - -namespace MailKit.Net.Imap { - /// - /// An IMAP event group used with the NOTIFY command. - /// - /// - /// An IMAP event group used with the NOTIFY command. - /// - public sealed class ImapEventGroup - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The mailbox filter. - /// The list of IMAP events. - /// - /// is null. - /// -or- - /// is null. - /// - public ImapEventGroup (ImapMailboxFilter mailboxFilter, IList events) - { - if (mailboxFilter == null) - throw new ArgumentNullException (nameof (mailboxFilter)); - - if (events == null) - throw new ArgumentNullException (nameof (events)); - - MailboxFilter = mailboxFilter; - Events = events; - } - - /// - /// Get the mailbox filter. - /// - /// - /// Gets the mailbox filter. - /// - /// The mailbox filter. - public ImapMailboxFilter MailboxFilter { - get; private set; - } - - /// - /// Get the list of IMAP events. - /// - /// - /// Gets the list of IMAP events. - /// - /// The events. - public IList Events { - get; private set; - } - - /// - /// Format the IMAP NOTIFY command for this particular IMAP event group. - /// - /// - /// Formats the IMAP NOTIFY command for this particular IMAP event group. - /// - /// The IMAP engine. - /// The IMAP command builder. - /// The IMAP command argument builder. - /// Gets set to true if the NOTIFY command requests the MessageNew or - /// MessageExpunged events for a SELECTED or SELECTED-DELAYED mailbox filter; otherwise it is left unchanged. - internal void Format (ImapEngine engine, StringBuilder command, IList args, ref bool notifySelectedNewExpunge) - { - bool isSelectedFilter = MailboxFilter == ImapMailboxFilter.Selected || MailboxFilter == ImapMailboxFilter.SelectedDelayed; - - command.Append ("("); - MailboxFilter.Format (engine, command, args); - command.Append (" "); - - if (Events.Count > 0) { - var haveAnnotationChange = false; - var haveMessageExpunge = false; - var haveMessageNew = false; - var haveFlagChange = false; - - command.Append ("("); - - for (int i = 0; i < Events.Count; i++) { - var @event = Events[i]; - - if (isSelectedFilter && !@event.IsMessageEvent) - throw new InvalidOperationException ("Only message events may be specified when SELECTED or SELECTED-DELAYED is used."); - - if (@event is ImapEvent.MessageNew) - haveMessageNew = true; - else if (@event == ImapEvent.MessageExpunge) - haveMessageExpunge = true; - else if (@event == ImapEvent.FlagChange) - haveFlagChange = true; - else if (@event == ImapEvent.AnnotationChange) - haveAnnotationChange = true; - - if (i > 0) - command.Append (" "); - - @event.Format (engine, command, args, isSelectedFilter); - } - command.Append (")"); - - // https://tools.ietf.org/html/rfc5465#section-5 - if ((haveMessageNew && !haveMessageExpunge) || (!haveMessageNew && haveMessageExpunge)) - throw new InvalidOperationException ("If MessageNew or MessageExpunge is specified, both must be specified."); - - if ((haveFlagChange || haveAnnotationChange) && (!haveMessageNew || !haveMessageExpunge)) - throw new InvalidOperationException ("If FlagChange and/or AnnotationChange are specified, MessageNew and MessageExpunge must also be specified."); - - notifySelectedNewExpunge = (haveMessageNew || haveMessageExpunge) && MailboxFilter == ImapMailboxFilter.Selected; - } else { - command.Append ("NONE"); - } - - command.Append (")"); - } - } - - /// - /// An IMAP mailbox filter for use with the NOTIFY command. - /// - /// - /// An IMAP mailbox filter for use with the NOTIFY command. - /// - public class ImapMailboxFilter - { - /// - /// An IMAP mailbox filter specifying that the client wants immediate notifications for - /// the currently selected folder. - /// - /// - /// The SELECTED mailbox specifier requires the server to send immediate - /// notifications for the currently selected mailbox about all specified - /// message events. - /// - public static readonly ImapMailboxFilter Selected = new ImapMailboxFilter ("SELECTED"); - - /// - /// An IMAP mailbox filter specifying the currently selected folder but delays notifications - /// until a command has been issued. - /// - /// - /// The SELECTED-DELAYED mailbox specifier requires the server to delay a - /// event until the client issues a command that allows - /// returning information about expunged messages (see - /// Section 7.4.1 of RFC3501] - /// for more details), for example, till a NOOP or an IDLE command has been issued. - /// When SELECTED-DELAYED is specified, the server MAY also delay returning other message - /// events until the client issues one of the commands specified above, or it MAY return them - /// immediately. - /// - public static readonly ImapMailboxFilter SelectedDelayed = new ImapMailboxFilter ("SELECTED-DELAYED"); - - /// - /// An IMAP mailbox filter specifying the currently selected folder. - /// - /// - /// The INBOXES mailbox specifier refers to all selectable mailboxes in the user's - /// personal namespace(s) to which messages may be delivered by a Message Delivery Agent (MDA). - /// - /// If the IMAP server cannot easily compute this set, it MUST treat - /// as equivalent to . - /// - public static readonly ImapMailboxFilter Inboxes = new ImapMailboxFilter ("INBOXES"); - - /// - /// An IMAP mailbox filter specifying all selectable folders within the user's personal namespace. - /// - /// - /// The PERSONAL mailbox specifier refers to all selectable folders within the user's personal namespace. - /// - public static readonly ImapMailboxFilter Personal = new ImapMailboxFilter ("PERSONAL"); - - /// - /// An IMAP mailbox filter that refers to all subscribed folders. - /// - /// - /// The SUBSCRIBED mailbox specifier refers to all folders subscribed to by the user. - /// If the subscription list changes, the server MUST reevaluate the list. - /// - public static readonly ImapMailboxFilter Subscribed = new ImapMailboxFilter ("SUBSCRIBED"); - - /// - /// An IMAP mailbox filter that specifies a list of folders to receive notifications about. - /// - /// - /// An IMAP mailbox filter that specifies a list of folders to receive notifications about. - /// - public class Mailboxes : ImapMailboxFilter - { - readonly ImapFolder[] folders; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The list of folders to watch for events. - /// - /// is null. - /// - /// - /// The list of is empty. - /// -or- - /// The list of contains folders that are not of - /// type . - /// - public Mailboxes (IList folders) : this ("MAILBOXES", folders) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The list of folders to watch for events. - /// - /// is null. - /// - /// - /// The list of is empty. - /// -or- - /// The list of contains folders that are not of - /// type . - /// - public Mailboxes (params IMailFolder[] folders) : this ("MAILBOXES", folders) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The name of the mailbox filter. - /// The list of folders to watch for events. - /// - /// is null. - /// - /// - /// The list of is empty. - /// -or- - /// The list of contains folders that are not of - /// type . - /// - internal Mailboxes (string name, IList folders) : base (name) - { - if (folders == null) - throw new ArgumentNullException (nameof (folders)); - - if (folders.Count == 0) - throw new ArgumentException ("Must supply at least one folder.", nameof (folders)); - - this.folders = new ImapFolder[folders.Count]; - for (int i = 0; i < folders.Count; i++) { - if (!(folders[i] is ImapFolder folder)) - throw new ArgumentException ("All folders must be ImapFolders.", nameof (folders)); - - this.folders[i] = folder; - } - } - - /// - /// Format the IMAP NOTIFY command for this particular IMAP mailbox filter. - /// - /// - /// Formats the IMAP NOTIFY command for this particular IMAP mailbox filter. - /// - /// The IMAP engine. - /// The IMAP command builder. - /// The IMAP command argument builder. - internal override void Format (ImapEngine engine, StringBuilder command, IList args) - { - command.Append (Name); - command.Append (' '); - - // FIXME: should we verify that each ImapFolder belongs to this ImapEngine? - - if (folders.Length == 1) { - command.Append ("%F"); - args.Add (folders[0]); - } else { - command.Append ("("); - - for (int i = 0; i < folders.Length; i++) { - if (i > 0) - command.Append (" "); - command.Append ("%F"); - args.Add (folders[i]); - } - - command.Append (")"); - } - } - } - - /// - /// An IMAP mailbox filter that specifies a list of folder subtrees to get notifications about. - /// - /// - /// The client will receive notifications for each specified folder plus all selectable - /// folders that are subordinate to any of the specified folders. - /// - public class Subtree : Mailboxes - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The list of folders to watch for events. - /// - /// is null. - /// - /// - /// The list of is empty. - /// -or- - /// The list of contains folders that are not of - /// type . - /// - public Subtree (IList folders) : base ("SUBTREE", folders) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The list of folders to watch for events. - /// - /// is null. - /// - /// - /// The list of is empty. - /// -or- - /// The list of contains folders that are not of - /// type . - /// - public Subtree (params IMailFolder[] folders) : base ("SUBTREE", folders) - { - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The name of the mailbox filter. - internal ImapMailboxFilter (string name) - { - Name = name; - } - - /// - /// Get the name of the mailbox filter. - /// - /// - /// Gets the name of the mailbox filter. - /// - /// The name. - public string Name { get; private set; } - - /// - /// Format the IMAP NOTIFY command for this particular IMAP mailbox filter. - /// - /// - /// Formats the IMAP NOTIFY command for this particular IMAP mailbox filter. - /// - /// The IMAP engine. - /// The IMAP command builder. - /// The IMAP command argument builder. - internal virtual void Format (ImapEngine engine, StringBuilder command, IList args) - { - command.Append (Name); - } - } - - /// - /// An IMAP notification event. - /// - /// - /// An IMAP notification event. - /// - public class ImapEvent - { - /// - /// An IMAP event notification for expunged messages. - /// - /// - /// If the expunged message or messages are in the selected mailbox, the server notifies the client - /// using (or if - /// the QRESYNC extension has been enabled via - /// or - /// ). - /// If the expunged message or messages are in another mailbox, the - /// and properties will be updated and the appropriate - /// and events will be - /// emitted for the relevant folder. If the QRESYNC - /// extension is enabled, the property will also be updated and - /// the event will be emitted. - /// if a client requests with the - /// mailbox specifier, the meaning of a message index can change at any time, so the client cannot use - /// message indexes in commands anymore. The client MUST use API variants that take or - /// a . The meaning of ** can also change when messages are added or expunged. - /// A client wishing to keep using message indexes can either use the - /// mailbox specifier or can avoid using the event entirely. - /// - public static readonly ImapEvent MessageExpunge = new ImapEvent ("MessageExpunge", true); - - /// - /// An IMAP event notification for message flag changes. - /// - /// - /// If the notification arrives for a message located in the currently selected - /// folder, then that folder will emit a event as well as a - /// event with an appropriately populated - /// . - /// On the other hand, if the notification arrives for a message that is not - /// located in the currently selected folder, then the events that are emitted will depend on the - /// of the IMAP server. - /// If the server supports the capability (or the - /// capability and the client has enabled it via - /// ), then the - /// event will be emitted as well as the - /// event (if the latter has changed). If the number of - /// seen messages has changed, then the event may also be emitted. - /// If the server does not support either the capability nor - /// the capability and the client has not enabled the later capability - /// via , then the server may choose - /// only to notify the client of changes by emitting the - /// event. - /// - public static readonly ImapEvent FlagChange = new ImapEvent ("FlagChange", true); - - /// - /// An IMAP event notification for message annotation changes. - /// - /// - /// If the notification arrives for a message located in the currently selected - /// folder, then that folder will emit a event as well as a - /// event with an appropriately populated - /// . - /// On the other hand, if the notification arrives for a message that is not - /// located in the currently selected folder, then the events that are emitted will depend on the - /// of the IMAP server. - /// If the server supports the capability (or the - /// capability and the client has enabled it via - /// ), then the - /// event will be emitted as well as the - /// event (if the latter has changed). If the number of - /// seen messages has changed, then the event may also be emitted. - /// If the server does not support either the capability nor - /// the capability and the client has not enabled the later capability - /// via , then the server may choose - /// only to notify the client of changes by emitting the - /// event. - /// - public static readonly ImapEvent AnnotationChange = new ImapEvent ("AnnotationChange", true); - - /// - /// AN IMAP event notification for folders that have been created, deleted, or renamed. - /// - /// - /// These notifications are sent if an affected mailbox name was created, deleted, or renamed. - /// As these notifications are received by the client, the apropriate will be emitted: - /// , , or - /// , respectively. - /// If the server supports , granting or revocation of the - /// right to the current user on the affected folder will also be - /// considered folder creation or deletion, respectively. If a folder is created or deleted, the folder itself - /// and its direct parent (whether it is an existing folder or not) are considered to be affected. - /// - public static readonly ImapEvent MailboxName = new ImapEvent ("MailboxName", false); - - /// - /// An IMAP event notification for folders who have had their subscription status changed. - /// - /// - /// This event requests that the server notifies the client of any subscription changes, - /// causing the or - /// events to be emitted accordingly on the affected . - /// - public static readonly ImapEvent SubscriptionChange = new ImapEvent ("SubscriptionChange", false); - - /// - /// An IMAP event notification for changes to folder metadata. - /// - /// - /// Support for this event type is OPTIONAL unless is supported - /// by the server, in which case support for this event type is REQUIRED. - /// If the server does support this event, then the event - /// will be emitted whenever metadata changes for any folder included in the . - /// - public static readonly ImapEvent MailboxMetadataChange = new ImapEvent ("MailboxMetadataChange", false); - - /// - /// An IMAP event notification for changes to server metadata. - /// - /// - /// Support for this event type is OPTIONAL unless is supported - /// by the server, in which case support for this event type is REQUIRED. - /// If the server does support this event, then the event - /// will be emitted whenever metadata changes. - /// - public static readonly ImapEvent ServerMetadataChange = new ImapEvent ("ServerMetadataChange", false); - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The name of the IMAP event. - /// true if the event is a message event; otherwise, false. - internal ImapEvent (string name, bool isMessageEvent) - { - IsMessageEvent = isMessageEvent; - Name = name; - } - - /// - /// Get whether or not this is a message event. - /// - /// - /// Gets whether or not this is a message event. - /// - /// true if is message event; otherwise, false. - internal bool IsMessageEvent { - get; private set; - } - - /// - /// Get the name of the IMAP event. - /// - /// - /// Gets the name of the IMAP event. - /// - /// The name of the IMAP event. - public string Name { - get; private set; - } - - /// - /// Format the IMAP NOTIFY command for this particular IMAP mailbox filter. - /// - /// - /// Formats the IMAP NOTIFY command for this particular IMAP mailbox filter. - /// - /// The IMAP engine. - /// The IMAP command builder. - /// The IMAP command argument builder. - /// true if the event is being registered for a - /// or - /// mailbox filter. - internal virtual void Format (ImapEngine engine, StringBuilder command, IList args, bool isSelectedFilter) - { - command.Append (Name); - } - - /// - /// An IMAP event notification for new or appended messages. - /// - /// - /// An IMAP event notification for new or appended messages. - /// If the new or appended message is in the selected folder, the folder will emit the - /// event, followed by a - /// event containing the information requested by the client. - /// These events will not be emitted for any message created by the client on this particular folder - /// as a result of, for example, a call to - /// - /// or . - /// - public class MessageNew : ImapEvent - { - readonly MessageSummaryItems items; - readonly HashSet headers; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The message summary items to automatically retrieve for new messages. - public MessageNew (MessageSummaryItems items = MessageSummaryItems.None) : base ("MessageNew", true) - { - headers = ImapFolder.EmptyHeaderFields; - this.items = items; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The message summary items to automatically retrieve for new messages. - /// Additional message headers to retrieve for new messages. - public MessageNew (MessageSummaryItems items, HashSet headers) : this (items) - { - this.headers = ImapUtils.GetUniqueHeaders (headers); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The message summary items to automatically retrieve for new messages. - /// Additional message headers to retrieve for new messages. - public MessageNew (MessageSummaryItems items, HashSet headers) : this (items) - { - this.headers = ImapUtils.GetUniqueHeaders (headers); - } - - /// - /// Format the IMAP NOTIFY command for this particular IMAP mailbox filter. - /// - /// - /// Formats the IMAP NOTIFY command for this particular IMAP mailbox filter. - /// - /// The IMAP engine. - /// The IMAP command builder. - /// The IMAP command argument builder. - /// true if the event is being registered for a - /// or - /// mailbox filter. - internal override void Format (ImapEngine engine, StringBuilder command, IList args, bool isSelectedFilter) - { - command.Append (Name); - - if (items == MessageSummaryItems.None && headers.Count == 0) - return; - - if (!isSelectedFilter) - throw new InvalidOperationException ("The MessageNew event cannot have any parameters for mailbox filters other than SELECTED and SELECTED-DELAYED."); - - var xitems = items; - bool previewText; - - command.Append (" "); - command.Append (ImapFolder.FormatSummaryItems (engine, ref xitems, headers, out previewText, isNotify: true)); - } - } - } -} diff --git a/src/MailKit/Net/Imap/ImapFolder.cs b/src/MailKit/Net/Imap/ImapFolder.cs deleted file mode 100644 index 8c4ae88..0000000 --- a/src/MailKit/Net/Imap/ImapFolder.cs +++ /dev/null @@ -1,6224 +0,0 @@ -// -// ImapFolder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -using MailKit.Search; - -namespace MailKit.Net.Imap { - /// - /// An IMAP folder. - /// - /// - /// An IMAP folder. - /// - /// - /// - /// - /// - /// - /// - public partial class ImapFolder : MailFolder, IImapFolder - { - bool supportsModSeq; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// If you subclass , you will also need to subclass - /// and override the - /// - /// method in order to return a new instance of your ImapFolder subclass. - /// - /// The constructor arguments. - /// - /// is null. - /// - public ImapFolder (ImapFolderConstructorArgs args) - { - if (args == null) - throw new ArgumentNullException (nameof (args)); - - InitializeProperties (args); - } - - void InitializeProperties (ImapFolderConstructorArgs args) - { - DirectorySeparator = args.DirectorySeparator; - EncodedName = args.EncodedName; - Attributes = args.Attributes; - FullName = args.FullName; - Engine = args.Engine; - Name = args.Name; - } - - /// - /// Get the IMAP command engine. - /// - /// - /// Gets the IMAP command engine. - /// - /// The engine. - internal ImapEngine Engine { - get; private set; - } - - /// - /// Get the encoded name of the folder. - /// - /// - /// Gets the encoded name of the folder. - /// - /// The encoded name. - internal string EncodedName { - get; set; - } - - /// - /// Gets an object that can be used to synchronize access to the IMAP server. - /// - /// - /// Gets an object that can be used to synchronize access to the IMAP server. - /// When using the non-Async methods from multiple threads, it is important to lock the - /// object for thread safety when using the synchronous methods. - /// - /// The lock object. - public override object SyncRoot { - get { return Engine; } - } - - /// - /// Get the threading algorithms supported by the folder. - /// - /// - /// Get the threading algorithms supported by the folder. - /// - /// The supported threading algorithms. - public override HashSet ThreadingAlgorithms { - get { return Engine.ThreadingAlgorithms; } - } - - /// - /// Determine whether or not an supports a feature. - /// - /// - /// Determines whether or not an supports a feature. - /// - /// The desired feature. - /// true if the feature is supported; otherwise, false. - public override bool Supports (FolderFeature feature) - { - switch (feature) { - case FolderFeature.AccessRights: return (Engine.Capabilities & ImapCapabilities.Acl) != 0; - case FolderFeature.Annotations: return AnnotationAccess != AnnotationAccess.None; - case FolderFeature.Metadata: return (Engine.Capabilities & ImapCapabilities.Metadata) != 0; - case FolderFeature.ModSequences: return supportsModSeq; - case FolderFeature.QuickResync: return Engine.QResyncEnabled; - case FolderFeature.Quotas: return (Engine.Capabilities & ImapCapabilities.Quota) != 0; - case FolderFeature.Sorting: return (Engine.Capabilities & ImapCapabilities.Sort) != 0; - case FolderFeature.Threading: return (Engine.Capabilities & ImapCapabilities.Thread) != 0; - case FolderFeature.UTF8: return Engine.UTF8Enabled; - default: return false; - } - } - - void CheckState (bool open, bool rw) - { - if (Engine.IsDisposed) - throw new ObjectDisposedException (nameof (ImapClient)); - - if (!Engine.IsConnected) - throw new ServiceNotConnectedException ("The ImapClient is not connected."); - - if (Engine.State < ImapEngineState.Authenticated) - throw new ServiceNotAuthenticatedException ("The ImapClient is not authenticated."); - - if (open) { - var access = rw ? FolderAccess.ReadWrite : FolderAccess.ReadOnly; - - if (!IsOpen || Access < access) - throw new FolderNotOpenException (FullName, access); - } - } - - void CheckAllowIndexes () - { - // Indexes ("Message Sequence Numbers" or MSNs in the RFCs) and * are not stable while MessageNew/MessageExpunge is registered for SELECTED and therefore should not be used - // https://tools.ietf.org/html/rfc5465#section-5.2 - if (Engine.NotifySelectedNewExpunge) - throw new InvalidOperationException ("Indexes and '*' cannot be used while MessageNew/MessageExpunge is registered with NOTIFY for SELECTED."); - } - - internal void Reset () - { - // basic state - PermanentFlags = MessageFlags.None; - AcceptedFlags = MessageFlags.None; - Access = FolderAccess.None; - - // annotate state - AnnotationAccess = AnnotationAccess.None; - AnnotationScopes = AnnotationScope.None; - MaxAnnotationSize = 0; - - // condstore state - supportsModSeq = false; - HighestModSeq = 0; - } - - /// - /// Notifies the folder that a parent folder has been renamed. - /// - /// - /// Updates the property. - /// - protected override void OnParentFolderRenamed () - { - var oldEncodedName = EncodedName; - - FullName = ParentFolder.FullName + DirectorySeparator + Name; - EncodedName = Engine.EncodeMailboxName (FullName); - Engine.FolderCache.Remove (oldEncodedName); - Engine.FolderCache[EncodedName] = this; - Reset (); - - if (Engine.Selected == this) { - Engine.State = ImapEngineState.Authenticated; - Engine.Selected = null; - OnClosed (); - } - } - - void ProcessResponseCodes (ImapCommand ic, IMailFolder folder, bool throwNotFound = true) - { - bool tryCreate = false; - - foreach (var code in ic.RespCodes) { - switch (code.Type) { - case ImapResponseCodeType.PermanentFlags: - PermanentFlags = ((PermanentFlagsResponseCode) code).Flags; - break; - case ImapResponseCodeType.ReadOnly: - if (code.IsTagged) - Access = FolderAccess.ReadOnly; - break; - case ImapResponseCodeType.ReadWrite: - if (code.IsTagged) - Access = FolderAccess.ReadWrite; - break; - case ImapResponseCodeType.TryCreate: - tryCreate = true; - break; - case ImapResponseCodeType.UidNext: - UidNext = ((UidNextResponseCode) code).Uid; - break; - case ImapResponseCodeType.UidValidity: - var uidValidity = ((UidValidityResponseCode) code).UidValidity; - if (IsOpen) - UpdateUidValidity (uidValidity); - else - UidValidity = uidValidity; - break; - case ImapResponseCodeType.Unseen: - FirstUnread = ((UnseenResponseCode) code).Index; - break; - case ImapResponseCodeType.HighestModSeq: - var highestModSeq = ((HighestModSeqResponseCode) code).HighestModSeq; - supportsModSeq = true; - if (IsOpen) - UpdateHighestModSeq (highestModSeq); - else - HighestModSeq = highestModSeq; - break; - case ImapResponseCodeType.NoModSeq: - supportsModSeq = false; - HighestModSeq = 0; - break; - case ImapResponseCodeType.MailboxId: - // Note: an untagged MAILBOX resp-code is returned on SELECT/EXAMINE while - // a *tagged* MAILBOXID resp-code is returned on CREATE. - if (!code.IsTagged) - Id = ((MailboxIdResponseCode) code).MailboxId; - break; - case ImapResponseCodeType.Annotations: - var annotations = (AnnotationsResponseCode) code; - AnnotationAccess = annotations.Access; - AnnotationScopes = annotations.Scopes; - MaxAnnotationSize = annotations.MaxSize; - break; - } - } - - if (tryCreate && throwNotFound && folder != null) - throw new FolderNotFoundException (folder.FullName); - } - - static ImapResponseCode GetResponseCode (ImapCommand ic, ImapResponseCodeType type) - { - for (int i = 0; i < ic.RespCodes.Count; i++) { - if (ic.RespCodes[i].Type == type) - return ic.RespCodes[i]; - } - - return null; - } - - #region IMailFolder implementation - - /// - /// Gets a value indicating whether the folder is currently open. - /// - /// - /// Gets a value indicating whether the folder is currently open. - /// - /// true if the folder is currently open; otherwise, false. - public override bool IsOpen { - get { return Engine.Selected == this; } - } - - static string SelectOrExamine (FolderAccess access) - { - return access == FolderAccess.ReadOnly ? "EXAMINE" : "SELECT"; - } - - static Task QResyncFetchAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - return ic.Folder.OnFetchAsync (engine, index, doAsync, ic.CancellationToken); - } - - async Task OpenAsync (ImapCommand ic, FolderAccess access, bool doAsync, CancellationToken cancellationToken) - { - Reset (); - - if (access == FolderAccess.ReadWrite) { - // Note: if the server does not respond with a PERMANENTFLAGS response, - // then we need to assume all flags are permanent. - PermanentFlags = SettableFlags | MessageFlags.UserDefined; - } else { - PermanentFlags = MessageFlags.None; - } - - try { - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, this); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create (access == FolderAccess.ReadOnly ? "EXAMINE" : "SELECT", ic); - } catch { - PermanentFlags = MessageFlags.None; - throw; - } - - if (Engine.Selected != null && Engine.Selected != this) { - var folder = Engine.Selected; - - folder.Reset (); - - folder.OnClosed (); - } - - Engine.State = ImapEngineState.Selected; - Engine.Selected = this; - - OnOpened (); - - return Access; - } - - Task OpenAsync (FolderAccess access, uint uidValidity, ulong highestModSeq, IList uids, bool doAsync, CancellationToken cancellationToken) - { - if (access != FolderAccess.ReadOnly && access != FolderAccess.ReadWrite) - throw new ArgumentOutOfRangeException (nameof (access)); - - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - CheckState (false, false); - - if ((Engine.Capabilities & ImapCapabilities.QuickResync) == 0) - throw new NotSupportedException ("The IMAP server does not support the QRESYNC extension."); - - if (!Supports (FolderFeature.QuickResync)) - throw new InvalidOperationException ("The QRESYNC extension has not been enabled."); - - string qresync; - - if ((Engine.Capabilities & ImapCapabilities.Annotate) != 0 && Engine.QuirksMode != ImapQuirksMode.SunMicrosystems) - qresync = string.Format (CultureInfo.InvariantCulture, "(ANNOTATE QRESYNC ({0} {1}", uidValidity, highestModSeq); - else - qresync = string.Format (CultureInfo.InvariantCulture, "(QRESYNC ({0} {1}", uidValidity, highestModSeq); - - if (uids.Count > 0) { - var set = UniqueIdSet.ToString (uids); - qresync += " " + set; - } - - qresync += "))"; - - var command = string.Format ("{0} %F {1}\r\n", SelectOrExamine (access), qresync); - var ic = new ImapCommand (Engine, cancellationToken, this, command, this); - ic.RegisterUntaggedHandler ("FETCH", QResyncFetchAsync); - - return OpenAsync (ic, access, doAsync, cancellationToken); - } - - /// - /// Open the folder using the requested folder access. - /// - /// - /// This variant of the - /// method is meant for quick resynchronization of the folder. Before calling this method, - /// the method MUST be called. - /// You should also make sure to add listeners to the and - /// events to get notifications of changes since - /// the last time the folder was opened. - /// - /// The state of the folder. - /// The requested folder access. - /// The last known value. - /// The last known value. - /// The last known list of unique message identifiers. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The QRESYNC feature has not been enabled. - /// - /// - /// The IMAP server does not support the QRESYNC extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override FolderAccess Open (FolderAccess access, uint uidValidity, ulong highestModSeq, IList uids, CancellationToken cancellationToken = default (CancellationToken)) - { - return OpenAsync (access, uidValidity, highestModSeq, uids, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously open the folder using the requested folder access. - /// - /// - /// This variant of the - /// method is meant for quick resynchronization of the folder. Before calling this method, - /// the method MUST be called. - /// You should also make sure to add listeners to the and - /// events to get notifications of changes since - /// the last time the folder was opened. - /// - /// The state of the folder. - /// The requested folder access. - /// The last known value. - /// The last known value. - /// The last known list of unique message identifiers. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The QRESYNC feature has not been enabled. - /// - /// - /// The IMAP server does not support the QRESYNC extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task OpenAsync (FolderAccess access, uint uidValidity, ulong highestModSeq, IList uids, CancellationToken cancellationToken = default (CancellationToken)) - { - return OpenAsync (access, uidValidity, highestModSeq, uids, true, cancellationToken); - } - - Task OpenAsync (FolderAccess access, bool doAsync, CancellationToken cancellationToken) - { - if (access != FolderAccess.ReadOnly && access != FolderAccess.ReadWrite) - throw new ArgumentOutOfRangeException (nameof (access)); - - CheckState (false, false); - - var @params = string.Empty; - - if ((Engine.Capabilities & ImapCapabilities.CondStore) != 0) - @params += "CONDSTORE"; - if ((Engine.Capabilities & ImapCapabilities.Annotate) != 0 && Engine.QuirksMode != ImapQuirksMode.SunMicrosystems) - @params += " ANNOTATE"; - - if (@params.Length > 0) - @params = " (" + @params.TrimStart () + ")"; - - var command = string.Format ("{0} %F{1}\r\n", SelectOrExamine (access), @params); - var ic = new ImapCommand (Engine, cancellationToken, this, command, this); - - return OpenAsync (ic, access, doAsync, cancellationToken); - } - - /// - /// Open the folder using the requested folder access. - /// - /// - /// Opens the folder using the requested folder access. - /// - /// The state of the folder. - /// The requested folder access. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override FolderAccess Open (FolderAccess access, CancellationToken cancellationToken = default (CancellationToken)) - { - return OpenAsync (access, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously open the folder using the requested folder access. - /// - /// - /// Opens the folder using the requested folder access. - /// - /// The state of the folder. - /// The requested folder access. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task OpenAsync (FolderAccess access, CancellationToken cancellationToken = default (CancellationToken)) - { - return OpenAsync (access, true, cancellationToken); - } - - async Task CloseAsync (bool expunge, bool doAsync, CancellationToken cancellationToken) - { - CheckState (true, expunge); - - ImapCommand ic; - - if (expunge) { - ic = Engine.QueueCommand (cancellationToken, this, "CLOSE\r\n"); - } else if ((Engine.Capabilities & ImapCapabilities.Unselect) != 0) { - ic = Engine.QueueCommand (cancellationToken, this, "UNSELECT\r\n"); - } else { - ic = null; - } - - if (ic != null) { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create (expunge ? "CLOSE" : "UNSELECT", ic); - } - - Reset (); - - if (Engine.Selected == this) { - Engine.State = ImapEngineState.Authenticated; - Engine.Selected = null; - OnClosed (); - } - } - - /// - /// Close the folder, optionally expunging the messages marked for deletion. - /// - /// - /// Closes the folder, optionally expunging the messages marked for deletion. - /// - /// If set to true, expunge. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Close (bool expunge = false, CancellationToken cancellationToken = default (CancellationToken)) - { - CloseAsync (expunge, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously close the folder, optionally expunging the messages marked for deletion. - /// - /// - /// Closes the folder, optionally expunging the messages marked for deletion. - /// - /// An asynchronous task context. - /// If set to true, expunge. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task CloseAsync (bool expunge = false, CancellationToken cancellationToken = default (CancellationToken)) - { - return CloseAsync (expunge, true, cancellationToken); - } - - async Task GetCreatedFolderAsync (string encodedName, string id, bool specialUse, bool doAsync, CancellationToken cancellationToken) - { - var ic = new ImapCommand (Engine, cancellationToken, null, "LIST \"\" %S\r\n", encodedName); - var list = new List (); - ImapFolder folder; - - ic.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderListAsync); - ic.UserData = list; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("LIST", ic); - - if ((folder = ImapEngine.GetFolder (list, encodedName)) != null) { - folder.ParentFolder = this; - folder.Id = id; - - if (specialUse) - Engine.AssignSpecialFolder (folder); - } - - return folder; - } - - async Task CreateAsync (string name, bool isMessageFolder, bool doAsync, CancellationToken cancellationToken) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (!Engine.IsValidMailboxName (name, DirectorySeparator)) - throw new ArgumentException ("The name is not a legal folder name.", nameof (name)); - - CheckState (false, false); - - if (!string.IsNullOrEmpty (FullName) && DirectorySeparator == '\0') - throw new InvalidOperationException ("Cannot create child folders."); - - var fullName = !string.IsNullOrEmpty (FullName) ? FullName + DirectorySeparator + name : name; - var encodedName = Engine.EncodeMailboxName (fullName); - var createName = encodedName; - - if (!isMessageFolder && Engine.QuirksMode != ImapQuirksMode.GMail) - createName += DirectorySeparator; - - var ic = Engine.QueueCommand (cancellationToken, null, "CREATE %S\r\n", createName); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok && GetResponseCode (ic, ImapResponseCodeType.AlreadyExists) == null) - throw ImapCommandException.Create ("CREATE", ic); - - var code = (MailboxIdResponseCode) GetResponseCode (ic, ImapResponseCodeType.MailboxId); - var id = code?.MailboxId; - - var created = await GetCreatedFolderAsync (encodedName, id, false, doAsync, cancellationToken).ConfigureAwait (false); - - Engine.OnFolderCreated (created); - - return created; - } - - /// - /// Create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// true if the folder will be used to contain messages; otherwise false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IMailFolder Create (string name, bool isMessageFolder, CancellationToken cancellationToken = default (CancellationToken)) - { - return CreateAsync (name, isMessageFolder, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// true if the folder will be used to contain messages; otherwise false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task CreateAsync (string name, bool isMessageFolder, CancellationToken cancellationToken = default (CancellationToken)) - { - return CreateAsync (name, isMessageFolder, true, cancellationToken); - } - - async Task CreateAsync (string name, IEnumerable specialUses, bool doAsync, CancellationToken cancellationToken) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (!Engine.IsValidMailboxName (name, DirectorySeparator)) - throw new ArgumentException ("The name is not a legal folder name.", nameof (name)); - - if (specialUses == null) - throw new ArgumentNullException (nameof (specialUses)); - - CheckState (false, false); - - if (!string.IsNullOrEmpty (FullName) && DirectorySeparator == '\0') - throw new InvalidOperationException ("Cannot create child folders."); - - if ((Engine.Capabilities & ImapCapabilities.CreateSpecialUse) == 0) - throw new NotSupportedException ("The IMAP server does not support the CREATE-SPECIAL-USE extension."); - - var uses = new StringBuilder (); - uint used = 0; - - foreach (var use in specialUses) { - var bit = (uint) (1 << ((int) use)); - - if ((used & bit) != 0) - continue; - - used |= bit; - - if (uses.Length > 0) - uses.Append (' '); - - switch (use) { - case SpecialFolder.All: uses.Append ("\\All"); break; - case SpecialFolder.Archive: uses.Append ("\\Archive"); break; - case SpecialFolder.Drafts: uses.Append ("\\Drafts"); break; - case SpecialFolder.Flagged: uses.Append ("\\Flagged"); break; - case SpecialFolder.Important: uses.Append ("\\Important"); break; - case SpecialFolder.Junk: uses.Append ("\\Junk"); break; - case SpecialFolder.Sent: uses.Append ("\\Sent"); break; - case SpecialFolder.Trash: uses.Append ("\\Trash"); break; - default: if (uses.Length > 0) uses.Length--; break; - } - } - - var fullName = !string.IsNullOrEmpty (FullName) ? FullName + DirectorySeparator + name : name; - var encodedName = Engine.EncodeMailboxName (fullName); - string command; - - if (uses.Length > 0) - command = string.Format ("CREATE %S (USE ({0}))\r\n", uses); - else - command = "CREATE %S\r\n"; - - var ic = Engine.QueueCommand (cancellationToken, null, command, encodedName); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("CREATE", ic); - - var code = (MailboxIdResponseCode) GetResponseCode (ic, ImapResponseCodeType.MailboxId); - var id = code?.MailboxId; - - var created = await GetCreatedFolderAsync (encodedName, id, true, doAsync, cancellationToken).ConfigureAwait (false); - - Engine.OnFolderCreated (created); - - return created; - } - - /// - /// Create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// A list of special uses for the folder being created. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The IMAP server does not support the CREATE-SPECIAL-USE extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IMailFolder Create (string name, IEnumerable specialUses, CancellationToken cancellationToken = default (CancellationToken)) - { - return CreateAsync (name, specialUses, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously create a new subfolder with the given name. - /// - /// - /// Creates a new subfolder with the given name. - /// - /// The created folder. - /// The name of the folder to create. - /// A list of special uses for the folder being created. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is nil, and thus child folders cannot be created. - /// - /// - /// The IMAP server does not support the CREATE-SPECIAL-USE extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task CreateAsync (string name, IEnumerable specialUses, CancellationToken cancellationToken = default (CancellationToken)) - { - return CreateAsync (name, specialUses, true, cancellationToken); - } - - async Task RenameAsync (IMailFolder parent, string name, bool doAsync, CancellationToken cancellationToken) - { - if (parent == null) - throw new ArgumentNullException (nameof (parent)); - - if (!(parent is ImapFolder) || ((ImapFolder) parent).Engine != Engine) - throw new ArgumentException ("The parent folder does not belong to this ImapClient.", nameof (parent)); - - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (!Engine.IsValidMailboxName (name, DirectorySeparator)) - throw new ArgumentException ("The name is not a legal folder name.", nameof (name)); - - if (IsNamespace || (Attributes & FolderAttributes.Inbox) != 0) - throw new InvalidOperationException ("Cannot rename this folder."); - - CheckState (false, false); - - string newFullName; - - if (!string.IsNullOrEmpty (parent.FullName)) - newFullName = parent.FullName + parent.DirectorySeparator + name; - else - newFullName = name; - - var encodedName = Engine.EncodeMailboxName (newFullName); - var ic = Engine.QueueCommand (cancellationToken, null, "RENAME %F %S\r\n", this, encodedName); - var oldFullName = FullName; - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, this); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("RENAME", ic); - - Engine.FolderCache.Remove (EncodedName); - Engine.FolderCache[encodedName] = this; - - ParentFolder = parent; - - FullName = Engine.DecodeMailboxName (encodedName); - EncodedName = encodedName; - Name = name; - - Reset (); - - if (Engine.Selected == this) { - Engine.State = ImapEngineState.Authenticated; - Engine.Selected = null; - OnClosed (); - } - - OnRenamed (oldFullName, FullName); - } - - /// - /// Rename the folder to exist with a new name under a new parent folder. - /// - /// - /// Renames the folder to exist with a new name under a new parent folder. - /// - /// The new parent folder. - /// The new name of the folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not belong to the . - /// -or- - /// is not a legal folder name. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The folder cannot be renamed (it is either a namespace or the Inbox). - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Rename (IMailFolder parent, string name, CancellationToken cancellationToken = default (CancellationToken)) - { - RenameAsync (parent, name, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously rename the folder to exist with a new name under a new parent folder. - /// - /// - /// Renames the folder to exist with a new name under a new parent folder. - /// - /// An awaitable task. - /// The new parent folder. - /// The new name of the folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not belong to the . - /// -or- - /// is not a legal folder name. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The folder cannot be renamed (it is either a namespace or the Inbox). - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task RenameAsync (IMailFolder parent, string name, CancellationToken cancellationToken = default (CancellationToken)) - { - return RenameAsync (parent, name, true, cancellationToken); - } - - async Task DeleteAsync (bool doAsync, CancellationToken cancellationToken) - { - if (IsNamespace || (Attributes & FolderAttributes.Inbox) != 0) - throw new InvalidOperationException ("Cannot delete this folder."); - - CheckState (false, false); - - var ic = Engine.QueueCommand (cancellationToken, null, "DELETE %F\r\n", this); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, this); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("DELETE", ic); - - Reset (); - - if (Engine.Selected == this) { - Engine.State = ImapEngineState.Authenticated; - Engine.Selected = null; - OnClosed (); - } - - Attributes |= FolderAttributes.NonExistent; - OnDeleted (); - } - - /// - /// Delete the folder on the IMAP server. - /// - /// - /// Deletes the folder on the IMAP server. - /// This method will not delete any child folders. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder cannot be deleted (it is either a namespace or the Inbox). - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Delete (CancellationToken cancellationToken = default (CancellationToken)) - { - DeleteAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously delete the folder on the IMAP server. - /// - /// - /// Deletes the folder on the IMAP server. - /// This method will not delete any child folders. - /// - /// An awaitable task. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The folder cannot be deleted (it is either a namespace or the Inbox). - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task DeleteAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return DeleteAsync (true, cancellationToken); - } - - async Task SubscribeAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckState (false, false); - - var ic = Engine.QueueCommand (cancellationToken, null, "SUBSCRIBE %F\r\n", this); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("SUBSCRIBE", ic); - - if ((Attributes & FolderAttributes.Subscribed) == 0) { - Attributes |= FolderAttributes.Subscribed; - - OnSubscribed (); - } - } - - /// - /// Subscribe the folder. - /// - /// - /// Subscribes the folder. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Subscribe (CancellationToken cancellationToken = default (CancellationToken)) - { - SubscribeAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously subscribe the folder. - /// - /// - /// Subscribes the folder. - /// - /// An awaitable task. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SubscribeAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return SubscribeAsync (true, cancellationToken); - } - - async Task UnsubscribeAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckState (false, false); - - var ic = Engine.QueueCommand (cancellationToken, null, "UNSUBSCRIBE %F\r\n", this); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("UNSUBSCRIBE", ic); - - if ((Attributes & FolderAttributes.Subscribed) != 0) { - Attributes &= ~FolderAttributes.Subscribed; - - OnUnsubscribed (); - } - } - - /// - /// Unsubscribe the folder. - /// - /// - /// Unsubscribes the folder. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Unsubscribe (CancellationToken cancellationToken = default (CancellationToken)) - { - UnsubscribeAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously unsubscribe the folder. - /// - /// - /// Unsubscribes the folder. - /// - /// An awaitable task. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task UnsubscribeAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return UnsubscribeAsync (true, cancellationToken); - } - - async Task> GetSubfoldersAsync (StatusItems items, bool subscribedOnly, bool doAsync, CancellationToken cancellationToken) - { - CheckState (false, 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 = new StringBuilder (EncodedName.Length + 2); - pattern.Append (EncodedName); - for (int i = 0; i < EncodedName.Length; i++) { - if (pattern[i] == '*') - pattern[i] = '%'; - } - if (pattern.Length > 0) - pattern.Append (DirectorySeparator); - pattern.Append ('%'); - - var children = new List (); - var status = items != StatusItems.None; - var list = new List (); - var command = new StringBuilder (); - var returnsSubscribed = false; - var lsub = subscribedOnly; - - if (subscribedOnly) { - if ((Engine.Capabilities & ImapCapabilities.ListExtended) != 0) { - command.Append ("LIST (SUBSCRIBED)"); - returnsSubscribed = true; - lsub = false; - } else { - command.Append ("LSUB"); - } - } else { - command.Append ("LIST"); - } - - command.Append (" \"\" %S"); - - if (!lsub) { - if (items != StatusItems.None && (Engine.Capabilities & ImapCapabilities.ListStatus) != 0) { - command.Append (" RETURN ("); - - if ((Engine.Capabilities & ImapCapabilities.ListExtended) != 0) { - if (!subscribedOnly) { - command.Append ("SUBSCRIBED "); - returnsSubscribed = true; - } - command.Append ("CHILDREN "); - } - - command.AppendFormat ("STATUS ({0})", Engine.GetStatusQuery (items)); - command.Append (')'); - status = false; - } else if ((Engine.Capabilities & ImapCapabilities.ListExtended) != 0) { - command.Append (" RETURN ("); - if (!subscribedOnly) { - command.Append ("SUBSCRIBED "); - returnsSubscribed = true; - } - command.Append ("CHILDREN"); - command.Append (')'); - } - } - - command.Append ("\r\n"); - - var ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), pattern.ToString ()); - ic.RegisterUntaggedHandler (lsub ? "LSUB" : "LIST", ImapUtils.ParseFolderListAsync); - ic.ListReturnsSubscribed = returnsSubscribed; - ic.UserData = list; - ic.Lsub = lsub; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - // Note: Due to the fact that folders can contain wildcards in them, we'll need to - // filter out any folders that are not children of this folder. - var prefix = FullName.Length > 0 ? FullName + DirectorySeparator : string.Empty; - prefix = ImapUtils.CanonicalizeMailboxName (prefix, DirectorySeparator); - var unparented = false; - - foreach (var folder in list) { - var canonicalFullName = ImapUtils.CanonicalizeMailboxName (folder.FullName, folder.DirectorySeparator); - var canonicalName = ImapUtils.IsInbox (folder.FullName) ? "INBOX" : folder.Name; - - if (!canonicalFullName.StartsWith (prefix, StringComparison.Ordinal)) { - unparented |= folder.ParentFolder == null; - continue; - } - - if (string.Compare (canonicalFullName, prefix.Length, canonicalName, 0, canonicalName.Length, StringComparison.Ordinal) != 0) { - unparented |= folder.ParentFolder == null; - continue; - } - - folder.ParentFolder = this; - children.Add (folder); - } - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create (lsub ? "LSUB" : "LIST", ic); - - // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their - // parent folders now so that they are not left in an inconsistent state. - if (unparented) - await Engine.LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false); - - if (status) { - for (int i = 0; i < children.Count; i++) { - if (children[i].Exists) - await ((ImapFolder) children[i]).StatusAsync (items, doAsync, false, cancellationToken).ConfigureAwait (false); - } - } - - return children; - } - - /// - /// Get the subfolders. - /// - /// - /// Gets the subfolders. - /// - /// The subfolders. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList GetSubfolders (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetSubfoldersAsync (items, subscribedOnly, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the subfolders. - /// - /// - /// Gets the subfolders. - /// - /// The subfolders. - /// The status items to pre-populate. - /// If set to true, only subscribed folders will be listed. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> GetSubfoldersAsync (StatusItems items, bool subscribedOnly = false, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetSubfoldersAsync (items, subscribedOnly, true, cancellationToken); - } - - async Task GetSubfolderAsync (string name, bool doAsync, CancellationToken cancellationToken) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (!Engine.IsValidMailboxName (name, DirectorySeparator)) - throw new ArgumentException ("The name of the subfolder is invalid.", nameof (name)); - - CheckState (false, false); - - var fullName = FullName.Length > 0 ? FullName + DirectorySeparator + name : name; - var encodedName = Engine.EncodeMailboxName (fullName); - List list; - ImapFolder folder; - - if (Engine.GetCachedFolder (encodedName, out folder)) - return folder; - - // 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.RegisterUntaggedHandler ("LIST", ImapUtils.ParseFolderListAsync); - ic.UserData = list = new List (); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("LIST", ic); - - if ((folder = ImapEngine.GetFolder (list, encodedName)) != null) - folder.ParentFolder = this; - - if (list.Count > 1 || folder == null) { - // Note: if any folders returned in the LIST command are unparented, have the ImapEngine look up their - // parent folders now so that they are not left in an inconsistent state. - await Engine.LookupParentFoldersAsync (list, doAsync, cancellationToken).ConfigureAwait (false); - } - - if (folder == null) - throw new FolderNotFoundException (fullName); - - return folder; - } - - /// - /// Get the specified subfolder. - /// - /// - /// Gets the specified subfolder. - /// - /// The subfolder. - /// The name of the subfolder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is either an empty string or contains the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The requested folder could not be found. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IMailFolder GetSubfolder (string name, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetSubfolderAsync (name, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified subfolder. - /// - /// - /// Gets the specified subfolder. - /// - /// The subfolder. - /// The name of the subfolder. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is either an empty string or contains the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The requested folder could not be found. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetSubfolderAsync (string name, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetSubfolderAsync (name, true, cancellationToken); - } - - async Task CheckAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckState (true, false); - - var ic = Engine.QueueCommand (cancellationToken, this, "CHECK\r\n"); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("CHECK", ic); - } - - /// - /// Force the server to sync its in-memory state with its disk state. - /// - /// - /// The CHECK command forces the IMAP server to sync its - /// in-memory state with its disk state. - /// For more information about the CHECK command, see - /// rfc350101. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not currently open. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Check (CancellationToken cancellationToken = default (CancellationToken)) - { - CheckAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously force the server to sync its in-memory state with its disk state. - /// - /// - /// The CHECK command forces the IMAP server to sync its - /// in-memory state with its disk state. - /// For more information about the CHECK command, see - /// rfc350101. - /// - /// An awaitable task. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not currently open. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task CheckAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return CheckAsync (true, cancellationToken); - } - - internal async Task StatusAsync (StatusItems items, bool doAsync, bool throwNotFound, CancellationToken cancellationToken) - { - if ((Engine.Capabilities & ImapCapabilities.Status) == 0) - throw new NotSupportedException ("The IMAP server does not support the STATUS command."); - - CheckState (false, false); - - if (items == StatusItems.None) - return; - - var command = string.Format ("STATUS %F ({0})\r\n", Engine.GetStatusQuery (items)); - var ic = Engine.QueueCommand (cancellationToken, null, command, this); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, this, throwNotFound); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("STATUS", ic); - } - - /// - /// Update the values of the specified items. - /// - /// - /// Updates the values of the specified items. - /// The method - /// MUST NOT be used on a folder that is already in the opened state. Instead, other ways - /// of getting the desired information should be used. - /// For example, a common use for the - /// method is to get the number of unread messages in the folder. When the folder is open, however, it is - /// possible to use the - /// method to query for the list of unread messages. - /// For more information about the STATUS command, see - /// rfc3501. - /// - /// The items to update. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The IMAP server does not support the STATUS command. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Status (StatusItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - StatusAsync (items, false, true, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously update the values of the specified items. - /// - /// - /// Updates the values of the specified items. - /// The method - /// MUST NOT be used on a folder that is already in the opened state. Instead, other ways - /// of getting the desired information should be used. - /// For example, a common use for the - /// method is to get the number of unread messages in the folder. When the folder is open, however, it is - /// possible to use the - /// method to query for the list of unread messages. - /// For more information about the STATUS command, see - /// rfc3501. - /// - /// An awaitable task. - /// The items to update. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The does not exist. - /// - /// - /// The IMAP server does not support the STATUS command. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task StatusAsync (StatusItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return StatusAsync (items, true, true, cancellationToken); - } - - static async Task ReadStringTokenAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - - switch (token.Type) { - case ImapTokenType.Literal: return await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - case ImapTokenType.QString: return (string) token.Value; - case ImapTokenType.Atom: return (string) token.Value; - default: - throw ImapEngine.UnexpectedToken (format, token); - } - } - - static async Task UntaggedAclAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ACL", "{0}"); - var acl = (AccessControlList) ic.UserData; - string name, rights; - ImapToken token; - - // read the mailbox name - await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - - do { - name = await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - rights = await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - - acl.Add (new AccessControl (name, rights)); - - token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } while (token.Type != ImapTokenType.Eoln); - } - - async Task GetAccessControlListAsync (bool doAsync, CancellationToken cancellationToken) - { - if ((Engine.Capabilities & ImapCapabilities.Acl) == 0) - throw new NotSupportedException ("The IMAP server does not support the ACL extension."); - - CheckState (false, false); - - var ic = new ImapCommand (Engine, cancellationToken, null, "GETACL %F\r\n", this); - ic.RegisterUntaggedHandler ("ACL", UntaggedAclAsync); - ic.UserData = new AccessControlList (); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("GETACL", ic); - - return (AccessControlList) ic.UserData; - } - - /// - /// Get the complete access control list for the folder. - /// - /// - /// Gets the complete access control list for the folder. - /// - /// The access control list. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override AccessControlList GetAccessControlList (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetAccessControlListAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the complete access control list for the folder. - /// - /// - /// Gets the complete access control list for the folder. - /// - /// The access control list. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override Task GetAccessControlListAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetAccessControlListAsync (true, cancellationToken); - } - - static async Task UntaggedListRightsAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "LISTRIGHTS", "{0}"); - var access = (AccessRights) ic.UserData; - ImapToken token; - - // read the mailbox name - await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - - // read the identity name - await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - - do { - var rights = await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - - access.AddRange (rights); - - token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } while (token.Type != ImapTokenType.Eoln); - } - - async Task GetAccessRightsAsync (string name, bool doAsync, CancellationToken cancellationToken) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if ((Engine.Capabilities & ImapCapabilities.Acl) == 0) - throw new NotSupportedException ("The IMAP server does not support the ACL extension."); - - CheckState (false, false); - - var ic = new ImapCommand (Engine, cancellationToken, null, "LISTRIGHTS %F %S\r\n", this, name); - ic.RegisterUntaggedHandler ("LISTRIGHTS", UntaggedListRightsAsync); - ic.UserData = new AccessRights (); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("LISTRIGHTS", ic); - - return (AccessRights) ic.UserData; - } - - /// - /// Get the access rights for a particular identifier. - /// - /// - /// Gets the access rights for a particular identifier. - /// - /// The access rights. - /// The identifier name. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override AccessRights GetAccessRights (string name, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetAccessRightsAsync (name, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the access rights for a particular identifier. - /// - /// - /// Gets the access rights for a particular identifier. - /// - /// The access rights. - /// The identifier name. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override Task GetAccessRightsAsync (string name, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetAccessRightsAsync (name, true, cancellationToken); - } - - static async Task UntaggedMyRightsAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - string format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "MYRIGHTS", "{0}"); - var access = (AccessRights) ic.UserData; - - // read the mailbox name - await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - - // read the access rights - access.AddRange (await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false)); - } - - async Task GetMyAccessRightsAsync (bool doAsync, CancellationToken cancellationToken) - { - if ((Engine.Capabilities & ImapCapabilities.Acl) == 0) - throw new NotSupportedException ("The IMAP server does not support the ACL extension."); - - CheckState (false, false); - - var ic = new ImapCommand (Engine, cancellationToken, null, "MYRIGHTS %F\r\n", this); - ic.RegisterUntaggedHandler ("MYRIGHTS", UntaggedMyRightsAsync); - ic.UserData = new AccessRights (); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("MYRIGHTS", ic); - - return (AccessRights) ic.UserData; - } - - /// - /// Get the access rights for the current authenticated user. - /// - /// - /// Gets the access rights for the current authenticated user. - /// - /// The access rights. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override AccessRights GetMyAccessRights (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMyAccessRightsAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the access rights for the current authenticated user. - /// - /// - /// Gets the access rights for the current authenticated user. - /// - /// The access rights. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override Task GetMyAccessRightsAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMyAccessRightsAsync (true, cancellationToken); - } - - async Task ModifyAccessRightsAsync (string name, AccessRights rights, string action, bool doAsync, CancellationToken cancellationToken) - { - if ((Engine.Capabilities & ImapCapabilities.Acl) == 0) - throw new NotSupportedException ("The IMAP server does not support the ACL extension."); - - CheckState (false, false); - - var ic = Engine.QueueCommand (cancellationToken, null, "SETACL %F %S %S\r\n", this, name, action + rights); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("SETACL", ic); - } - - /// - /// Add access rights for the specified identity. - /// - /// - /// Adds the given access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// No rights were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override void AddAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (rights == null) - throw new ArgumentNullException (nameof (rights)); - - if (rights.Count == 0) - throw new ArgumentException ("No rights were specified.", nameof (rights)); - - ModifyAccessRightsAsync (name, rights, "+", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously add access rights for the specified identity. - /// - /// - /// Adds the given access rights for the specified identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// No rights were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override Task AddAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (rights == null) - throw new ArgumentNullException (nameof (rights)); - - if (rights.Count == 0) - throw new ArgumentException ("No rights were specified.", nameof (rights)); - - return ModifyAccessRightsAsync (name, rights, "+", true, cancellationToken); - } - - /// - /// Remove access rights for the specified identity. - /// - /// - /// Removes the given access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// No rights were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override void RemoveAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (rights == null) - throw new ArgumentNullException (nameof (rights)); - - if (rights.Count == 0) - throw new ArgumentException ("No rights were specified.", nameof (rights)); - - ModifyAccessRightsAsync (name, rights, "-", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously remove access rights for the specified identity. - /// - /// - /// Removes the given access rights for the specified identity. - /// - /// An asynchronous task context. - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// No rights were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override Task RemoveAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (rights == null) - throw new ArgumentNullException (nameof (rights)); - - if (rights.Count == 0) - throw new ArgumentException ("No rights were specified.", nameof (rights)); - - return ModifyAccessRightsAsync (name, rights, "-", true, cancellationToken); - } - - /// - /// Set the access rights for the specified identity. - /// - /// - /// Sets the access rights for the specified identity. - /// - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override void SetAccessRights (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (rights == null) - throw new ArgumentNullException (nameof (rights)); - - ModifyAccessRightsAsync (name, rights, string.Empty, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the access rights for the specified identity. - /// - /// - /// Sets the access rights for the specified identity. - /// - /// An awaitable task. - /// The identity name. - /// The access rights. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override Task SetAccessRightsAsync (string name, AccessRights rights, CancellationToken cancellationToken = default (CancellationToken)) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (rights == null) - throw new ArgumentNullException (nameof (rights)); - - return ModifyAccessRightsAsync (name, rights, string.Empty, true, cancellationToken); - } - - async Task RemoveAccessAsync (string name, bool doAsync, CancellationToken cancellationToken) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if ((Engine.Capabilities & ImapCapabilities.Acl) == 0) - throw new NotSupportedException ("The IMAP server does not support the ACL extension."); - - CheckState (false, false); - - var ic = Engine.QueueCommand (cancellationToken, null, "DELETEACL %F %S\r\n", this, name); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("DELETEACL", ic); - } - - /// - /// Remove all access rights for the given identity. - /// - /// - /// Removes all access rights for the given identity. - /// - /// The identity name. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override void RemoveAccess (string name, CancellationToken cancellationToken = default (CancellationToken)) - { - RemoveAccessAsync (name, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously remove all access rights for the given identity. - /// - /// - /// Removes all access rights for the given identity. - /// - /// An awaitable task. - /// The identity name. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the ACL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The command failed. - /// - public override Task RemoveAccessAsync (string name, CancellationToken cancellationToken = default (CancellationToken)) - { - return RemoveAccessAsync (name, true, cancellationToken); - } - - async Task GetMetadataAsync (MetadataTag tag, bool doAsync, CancellationToken cancellationToken) - { - CheckState (false, false); - - if ((Engine.Capabilities & ImapCapabilities.Metadata) == 0) - throw new NotSupportedException ("The IMAP server does not support the METADATA extension."); - - var ic = new ImapCommand (Engine, cancellationToken, null, "GETMETADATA %F %S\r\n", this, tag.Id); - ic.RegisterUntaggedHandler ("METADATA", ImapUtils.ParseMetadataAsync); - var metadata = new MetadataCollection (); - ic.UserData = metadata; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("GETMETADATA", ic); - - string value = null; - - for (int i = 0; i < metadata.Count; i++) { - if (metadata[i].EncodedName == EncodedName && metadata[i].Tag.Id == tag.Id) { - value = metadata[i].Value; - metadata.RemoveAt (i); - break; - } - } - - Engine.ProcessMetadataChanges (metadata); - - return value; - } - - /// - /// Get the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override string GetMetadata (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (tag, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata value. - /// The metadata tag. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetMetadataAsync (MetadataTag tag, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (tag, true, cancellationToken); - } - - async Task GetMetadataAsync (MetadataOptions options, IEnumerable tags, bool doAsync, CancellationToken cancellationToken) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (tags == null) - throw new ArgumentNullException (nameof (tags)); - - CheckState (false, false); - - if ((Engine.Capabilities & ImapCapabilities.Metadata) == 0) - throw new NotSupportedException ("The IMAP server does not support the METADATA extension."); - - var command = new StringBuilder ("GETMETADATA %F"); - var args = new List (); - bool hasOptions = false; - - if (options.MaxSize.HasValue || options.Depth != 0) { - command.Append (" ("); - if (options.MaxSize.HasValue) - command.AppendFormat ("MAXSIZE {0} ", options.MaxSize.Value); - if (options.Depth > 0) - command.AppendFormat ("DEPTH {0} ", options.Depth == int.MaxValue ? "infinity" : "1"); - command[command.Length - 1] = ')'; - command.Append (' '); - hasOptions = true; - } - - args.Add (this); - - int startIndex = command.Length; - foreach (var tag in tags) { - command.Append (" %S"); - args.Add (tag.Id); - } - - if (hasOptions) { - command[startIndex] = '('; - command.Append (')'); - } - - command.Append ("\r\n"); - - if (args.Count == 1) - return new MetadataCollection (); - - var ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), args.ToArray ()); - ic.RegisterUntaggedHandler ("METADATA", ImapUtils.ParseMetadataAsync); - ic.UserData = new MetadataCollection (); - options.LongEntries = 0; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("GETMETADATA", ic); - - var metadata = (MetadataResponseCode) GetResponseCode (ic, ImapResponseCodeType.Metadata); - if (metadata != null && metadata.SubType == MetadataResponseCodeSubType.LongEntries) - options.LongEntries = metadata.Value; - - return Engine.FilterMetadata ((MetadataCollection) ic.UserData, EncodedName); - } - - /// - /// Get the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override MetadataCollection GetMetadata (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (options, tags, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified metadata. - /// - /// - /// Gets the specified metadata. - /// - /// The requested metadata. - /// The metadata options. - /// The metadata tags. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetMetadataAsync (MetadataOptions options, IEnumerable tags, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMetadataAsync (options, tags, true, cancellationToken); - } - - async Task SetMetadataAsync (MetadataCollection metadata, bool doAsync, CancellationToken cancellationToken) - { - if (metadata == null) - throw new ArgumentNullException (nameof (metadata)); - - CheckState (false, false); - - if ((Engine.Capabilities & ImapCapabilities.Metadata) == 0) - throw new NotSupportedException ("The IMAP server does not support the METADATA extension."); - - if (metadata.Count == 0) - return; - - var command = new StringBuilder ("SETMETADATA %F ("); - var args = new List (); - - args.Add (this); - - for (int i = 0; i < metadata.Count; i++) { - if (i > 0) - command.Append (' '); - - if (metadata[i].Value != null) { - command.Append ("%S %S"); - args.Add (metadata[i].Tag.Id); - args.Add (metadata[i].Value); - } else { - command.Append ("%S NIL"); - args.Add (metadata[i].Tag.Id); - } - } - command.Append (")\r\n"); - - var ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), args.ToArray ()); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("SETMETADATA", ic); - } - - /// - /// Set the specified metadata. - /// - /// - /// Sets the specified metadata. - /// - /// The metadata. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void SetMetadata (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)) - { - SetMetadataAsync (metadata, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously set the specified metadata. - /// - /// - /// Sets the specified metadata. - /// - /// An asynchronous task context. - /// The metadata. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the METADATA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SetMetadataAsync (MetadataCollection metadata, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetMetadataAsync (metadata, true, cancellationToken); - } - - class Quota - { - public uint? MessageLimit; - public uint? StorageLimit; - public uint? CurrentMessageCount; - public uint? CurrentStorageSize; - } - - class QuotaContext - { - public QuotaContext () - { - Quotas = new Dictionary (); - QuotaRoots = new List (); - } - - public IList QuotaRoots { - get; private set; - } - - public IDictionary Quotas { - get; private set; - } - } - - static async Task UntaggedQuotaRootAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "QUOTAROOT", "{0}"); - var ctx = (QuotaContext) ic.UserData; - - // The first token should be the mailbox name - await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - - // ...followed by 0 or more quota roots - var token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - while (token.Type != ImapTokenType.Eoln) { - var root = await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - ctx.QuotaRoots.Add (root); - - token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } - } - - static async Task UntaggedQuotaAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "QUOTA", "{0}"); - var quotaRoot = await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - var ctx = (QuotaContext) ic.UserData; - var quota = new Quota (); - - var token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - while (token.Type != ImapTokenType.CloseParen) { - uint used, limit; - string resource; - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.Atom, format, token); - - resource = (string) token.Value; - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - used = ImapEngine.ParseNumber (token, false, format, token); - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - limit = ImapEngine.ParseNumber (token, false, format, token); - - switch (resource.ToUpperInvariant ()) { - case "MESSAGE": - quota.CurrentMessageCount = used; - quota.MessageLimit = limit; - break; - case "STORAGE": - quota.CurrentStorageSize = used; - quota.StorageLimit = limit; - break; - } - - token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } - - // read the closing paren - await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - ctx.Quotas[quotaRoot] = quota; - } - - async Task GetQuotaAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckState (false, false); - - if ((Engine.Capabilities & ImapCapabilities.Quota) == 0) - throw new NotSupportedException ("The IMAP server does not support the QUOTA extension."); - - var ic = new ImapCommand (Engine, cancellationToken, null, "GETQUOTAROOT %F\r\n", this); - var ctx = new QuotaContext (); - - ic.RegisterUntaggedHandler ("QUOTAROOT", UntaggedQuotaRootAsync); - ic.RegisterUntaggedHandler ("QUOTA", UntaggedQuotaAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("GETQUOTAROOT", ic); - - for (int i = 0; i < ctx.QuotaRoots.Count; i++) { - var encodedName = ctx.QuotaRoots[i]; - ImapFolder quotaRoot; - Quota quota; - - if (!ctx.Quotas.TryGetValue (encodedName, out quota)) - continue; - - quotaRoot = await Engine.GetQuotaRootFolderAsync (encodedName, doAsync, cancellationToken).ConfigureAwait (false); - - return new FolderQuota (quotaRoot) { - CurrentMessageCount = quota.CurrentMessageCount, - CurrentStorageSize = quota.CurrentStorageSize, - MessageLimit = quota.MessageLimit, - StorageLimit = quota.StorageLimit - }; - } - - return new FolderQuota (null); - } - - /// - /// Get the quota information for the folder. - /// - /// - /// Gets the quota information for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The folder quota. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the QUOTA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override FolderQuota GetQuota (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetQuotaAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the quota information for the folder. - /// - /// - /// Gets the quota information for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The folder quota. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the QUOTA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetQuotaAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetQuotaAsync (true, cancellationToken); - } - - async Task SetQuotaAsync (uint? messageLimit, uint? storageLimit, bool doAsync, CancellationToken cancellationToken) - { - CheckState (false, false); - - if ((Engine.Capabilities & ImapCapabilities.Quota) == 0) - throw new NotSupportedException ("The IMAP server does not support the QUOTA extension."); - - var command = new StringBuilder ("SETQUOTA %F ("); - if (messageLimit.HasValue) - command.AppendFormat ("MESSAGE {0} ", messageLimit.Value); - if (storageLimit.HasValue) - command.AppendFormat ("STORAGE {0} ", storageLimit.Value); - command[command.Length - 1] = ')'; - command.Append ("\r\n"); - - var ic = new ImapCommand (Engine, cancellationToken, null, command.ToString (), this); - var ctx = new QuotaContext (); - Quota quota; - - ic.RegisterUntaggedHandler ("QUOTA", UntaggedQuotaAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("SETQUOTA", ic); - - if (ctx.Quotas.TryGetValue (EncodedName, out quota)) { - return new FolderQuota (this) { - CurrentMessageCount = quota.CurrentMessageCount, - CurrentStorageSize = quota.CurrentStorageSize, - MessageLimit = quota.MessageLimit, - StorageLimit = quota.StorageLimit - }; - } - - return new FolderQuota (null); - } - - /// - /// Set the quota limits for the folder. - /// - /// - /// Sets the quota limits for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The folder quota. - /// If not null, sets the maximum number of messages to allow. - /// If not null, sets the maximum storage size (in kilobytes). - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the QUOTA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override FolderQuota SetQuota (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetQuotaAsync (messageLimit, storageLimit, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously set the quota limits for the folder. - /// - /// - /// Sets the quota limits for the folder. - /// To determine if a quotas are supported, check the - /// property. - /// - /// The folder quota. - /// If not null, sets the maximum number of messages to allow. - /// If not null, sets the maximum storage size (in kilobytes). - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The IMAP server does not support the QUOTA extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SetQuotaAsync (uint? messageLimit, uint? storageLimit, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetQuotaAsync (messageLimit, storageLimit, true, cancellationToken); - } - - async Task ExpungeAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckState (true, true); - - var ic = Engine.QueueCommand (cancellationToken, this, "EXPUNGE\r\n"); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("EXPUNGE", ic); - } - - /// - /// Expunge the folder, permanently removing all messages marked for deletion. - /// - /// - /// The EXPUNGE command permanently removes all messages in the folder - /// that have the flag set. - /// For more information about the EXPUNGE command, see - /// rfc3501. - /// Normally, a event will be emitted - /// for each message that is expunged. However, if the IMAP server supports the QRESYNC extension - /// and it has been enabled via the - /// method, then the event will be emitted rather than - /// the event. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Expunge (CancellationToken cancellationToken = default (CancellationToken)) - { - ExpungeAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously expunge the folder, permanently removing all messages marked for deletion. - /// - /// - /// The EXPUNGE command permanently removes all messages in the folder - /// that have the flag set. - /// For more information about the EXPUNGE command, see - /// rfc3501. - /// Normally, a event will be emitted - /// for each message that is expunged. However, if the IMAP server supports the QRESYNC extension - /// and it has been enabled via the - /// method, then the event will be emitted rather than - /// the event. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task ExpungeAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return ExpungeAsync (true, cancellationToken); - } - - async Task ExpungeAsync (IList uids, bool doAsync, CancellationToken cancellationToken) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - CheckState (true, true); - - if (uids.Count == 0) - return; - - if ((Engine.Capabilities & ImapCapabilities.UidPlus) == 0) { - // get the list of messages marked for deletion that should not be expunged - var query = SearchQuery.Deleted.And (SearchQuery.Not (SearchQuery.Uids (uids))); - var unmark = await SearchAsync (query, doAsync, false, cancellationToken).ConfigureAwait (false); - - if (unmark.Count > 0) { - // clear the \Deleted flag on all messages except the ones that are to be expunged - await ModifyFlagsAsync (unmark, null, MessageFlags.Deleted, null, "-FLAGS.SILENT", doAsync, cancellationToken).ConfigureAwait (false); - } - - // expunge the folder - await ExpungeAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (unmark.Count > 0) { - // restore the \Deleted flags - await ModifyFlagsAsync (unmark, null, MessageFlags.Deleted, null, "+FLAGS.SILENT", doAsync, cancellationToken).ConfigureAwait (false); - } - - return; - } - - foreach (var ic in Engine.QueueCommands (cancellationToken, this, "UID EXPUNGE %s\r\n", uids)) { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("EXPUNGE", ic); - } - } - - /// - /// Expunge the specified uids, permanently removing them from the folder. - /// - /// - /// Expunges the specified uids, permanently removing them from the folder. - /// If the IMAP server supports the UIDPLUS extension (check the - /// for the - /// flag), then this operation is atomic. Otherwise, MailKit implements this operation - /// by first searching for the full list of message uids in the folder that are marked for - /// deletion, unmarking the set of message uids that are not within the specified list of - /// uids to be be expunged, expunging the folder (thus expunging the requested uids), and - /// finally restoring the deleted flag on the collection of message uids that were originally - /// marked for deletion that were not included in the list of uids provided. For this reason, - /// it is advisable for clients that wish to maintain state to implement this themselves when - /// the IMAP server does not support the UIDPLUS extension. - /// For more information about the UID EXPUNGE command, see - /// rfc4315. - /// Normally, a event will be emitted - /// for each message that is expunged. However, if the IMAP server supports the QRESYNC extension - /// and it has been enabled via the - /// method, then the event will be emitted rather than - /// the event. - /// - /// The message uids. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Expunge (IList uids, CancellationToken cancellationToken = default (CancellationToken)) - { - ExpungeAsync (uids, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously expunge the specified uids, permanently removing them from the folder. - /// - /// - /// Expunges the specified uids, permanently removing them from the folder. - /// If the IMAP server supports the UIDPLUS extension (check the - /// for the - /// flag), then this operation is atomic. Otherwise, MailKit implements this operation - /// by first searching for the full list of message uids in the folder that are marked for - /// deletion, unmarking the set of message uids that are not within the specified list of - /// uids to be be expunged, expunging the folder (thus expunging the requested uids), and - /// finally restoring the deleted flag on the collection of message uids that were originally - /// marked for deletion that were not included in the list of uids provided. For this reason, - /// it is advisable for clients that wish to maintain state to implement this themselves when - /// the IMAP server does not support the UIDPLUS extension. - /// For more information about the UID EXPUNGE command, see - /// rfc4315. - /// Normally, a event will be emitted - /// for each message that is expunged. However, if the IMAP server supports the QRESYNC extension - /// and it has been enabled via the - /// method, then the event will be emitted rather than - /// the event. - /// - /// An asynchronous task context. - /// The message uids. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task ExpungeAsync (IList uids, CancellationToken cancellationToken = default (CancellationToken)) - { - return ExpungeAsync (uids, true, cancellationToken); - } - - ImapCommand QueueAppend (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken, ITransferProgress progress) - { - var builder = new StringBuilder ("APPEND %F "); - var list = new List (); - - list.Add (this); - - if ((flags & SettableFlags) != 0) - builder.AppendFormat ("{0} ", ImapUtils.FormatFlagsList (flags, 0)); - - if (date.HasValue) - builder.AppendFormat ("\"{0}\" ", ImapUtils.FormatInternalDate (date.Value)); - - if (annotations != null && annotations.Count > 0) { - ImapUtils.FormatAnnotations (builder, annotations, list, false); - - if (builder[builder.Length - 1] != ' ') - builder.Append (' '); - } - - builder.Append ("%L\r\n"); - list.Add (message); - - var command = builder.ToString (); - var args = list.ToArray (); - - var ic = new ImapCommand (Engine, cancellationToken, null, options, command, args); - ic.Progress = progress; - - Engine.QueueCommand (ic); - - return ic; - } - - async Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - CheckState (false, false); - - if (options.International && (Engine.Capabilities & ImapCapabilities.UTF8Accept) == 0) - throw new NotSupportedException ("The IMAP server does not support the UTF8 extension."); - - var format = options.Clone (); - format.NewLineFormat = NewLineFormat.Dos; - format.EnsureNewLine = true; - - if ((Engine.Capabilities & ImapCapabilities.UTF8Only) == ImapCapabilities.UTF8Only) - format.International = true; - - if (format.International && !Engine.UTF8Enabled) - throw new InvalidOperationException ("The UTF8 extension has not been enabled."); - - var ic = QueueAppend (format, message, flags, date, annotations, cancellationToken, progress); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, this); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("APPEND", ic); - - var append = (AppendUidResponseCode) GetResponseCode (ic, ImapResponseCodeType.AppendUid); - - if (append != null) - return append.UidSet[0]; - - return null; - } - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, message, flags, null, null, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, message, flags, null, null, true, cancellationToken, progress); - } - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, message, flags, date, null, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, message, flags, date, null, true, cancellationToken, progress); - } - - /// - /// Append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueId? Append (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, message, flags, date, annotations, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously append the specified message to the folder. - /// - /// - /// Appends the specified message to the folder and returns the UniqueId assigned to the message. - /// - /// The UID of the appended message, if available; otherwise, null. - /// The formatting options. - /// The message. - /// The message flags. - /// The received date of the message. - /// The message annotations. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task AppendAsync (FormatOptions options, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, message, flags, date, annotations, true, cancellationToken, progress); - } - - ImapCommand QueueMultiAppend (FormatOptions options, IList messages, IList flags, IList dates, IList> annotations, CancellationToken cancellationToken, ITransferProgress progress) - { - var builder = new StringBuilder ("APPEND %F"); - var list = new List (); - - list.Add (this); - - for (int i = 0; i < messages.Count; i++) { - builder.Append (' '); - - if ((flags[i] & SettableFlags) != 0) - builder.AppendFormat ("{0} ", ImapUtils.FormatFlagsList (flags[i], 0)); - - if (dates != null) - builder.AppendFormat ("\"{0}\" ", ImapUtils.FormatInternalDate (dates[i])); - - //if (annotations != null && annotations[i] != null && annotations[i].Count > 0) { - // ImapUtils.FormatAnnotations (builder, annotations[i], list, false); - - // if (builder[builder.Length - 1] != ' ') - // builder.Append (' '); - //} - - builder.Append ("%L"); - list.Add (messages[i]); - } - - builder.Append ("\r\n"); - - var command = builder.ToString (); - var args = list.ToArray (); - - var ic = new ImapCommand (Engine, cancellationToken, null, options, command, args); - ic.Progress = progress; - - Engine.QueueCommand (ic); - - return ic; - } - - async Task> AppendAsync (FormatOptions options, IList messages, IList flags, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (messages == null) - throw new ArgumentNullException (nameof (messages)); - - for (int i = 0; i < messages.Count; i++) { - if (messages[i] == null) - throw new ArgumentException ("One or more of the messages is null."); - } - - if (flags == null) - throw new ArgumentNullException (nameof (flags)); - - if (messages.Count != flags.Count) - throw new ArgumentException ("The number of messages and the number of flags must be equal."); - - CheckState (false, false); - - if (options.International && (Engine.Capabilities & ImapCapabilities.UTF8Accept) == 0) - throw new NotSupportedException ("The IMAP server does not support the UTF8 extension."); - - var format = options.Clone (); - format.NewLineFormat = NewLineFormat.Dos; - format.EnsureNewLine = true; - - if ((Engine.Capabilities & ImapCapabilities.UTF8Only) == ImapCapabilities.UTF8Only) - format.International = true; - - if (format.International && !Engine.UTF8Enabled) - throw new InvalidOperationException ("The UTF8 extension has not been enabled."); - - if (messages.Count == 0) - return new UniqueId[0]; - - if ((Engine.Capabilities & ImapCapabilities.MultiAppend) != 0) { - var ic = QueueMultiAppend (format, messages, flags, null, null, cancellationToken, progress); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, this); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("APPEND", ic); - - var append = (AppendUidResponseCode) GetResponseCode (ic, ImapResponseCodeType.AppendUid); - - if (append != null) - return append.UidSet; - - return new UniqueId[0]; - } - - // FIXME: use an aggregate progress reporter - var uids = new List (); - - for (int i = 0; i < messages.Count; i++) { - var uid = await AppendAsync (format, messages[i], flags[i], null, null, doAsync, cancellationToken, progress).ConfigureAwait (false); - if (uids != null && uid.HasValue) - uids.Add (uid.Value); - else - uids = null; - } - - if (uids == null) - return new UniqueId[0]; - - return uids; - } - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The list of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages does not match the number of flags. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Append (FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, messages, flags, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The list of messages to append to the folder. - /// The message flags to use for each message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages does not match the number of flags. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> AppendAsync (FormatOptions options, IList messages, IList flags, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, messages, flags, true, cancellationToken, progress); - } - - async Task> AppendAsync (FormatOptions options, IList messages, IList flags, IList dates, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (messages == null) - throw new ArgumentNullException (nameof (messages)); - - for (int i = 0; i < messages.Count; i++) { - if (messages[i] == null) - throw new ArgumentException ("One or more of the messages is null."); - } - - if (flags == null) - throw new ArgumentNullException (nameof (flags)); - - if (dates == null) - throw new ArgumentNullException (nameof (dates)); - - if (messages.Count != flags.Count || messages.Count != dates.Count) - throw new ArgumentException ("The number of messages, the number of flags, and the number of dates must be equal."); - - CheckState (false, false); - - if (options.International && (Engine.Capabilities & ImapCapabilities.UTF8Accept) == 0) - throw new NotSupportedException ("The IMAP server does not support the UTF8 extension."); - - var format = options.Clone (); - format.NewLineFormat = NewLineFormat.Dos; - format.EnsureNewLine = true; - - if ((Engine.Capabilities & ImapCapabilities.UTF8Only) == ImapCapabilities.UTF8Only) - format.International = true; - - if (format.International && !Engine.UTF8Enabled) - throw new InvalidOperationException ("The UTF8 extension has not been enabled."); - - if (messages.Count == 0) - return new UniqueId[0]; - - if ((Engine.Capabilities & ImapCapabilities.MultiAppend) != 0) { - var ic = QueueMultiAppend (format, messages, flags, dates, null, cancellationToken, progress); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("APPEND", ic); - - var append = (AppendUidResponseCode) GetResponseCode (ic, ImapResponseCodeType.AppendUid); - - if (append != null) - return append.UidSet; - - return new UniqueId[0]; - } - - // FIXME: use an aggregate progress reporter - var uids = new List (); - - for (int i = 0; i < messages.Count; i++) { - var uid = await AppendAsync (format, messages[i], flags[i], dates[i], null, doAsync, cancellationToken, progress).ConfigureAwait (false); - if (uids != null && uid.HasValue) - uids.Add (uid.Value); - else - uids = null; - } - - if (uids == null) - return new UniqueId[0]; - - return uids; - } - - /// - /// Append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The list of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages, flags, and dates do not match. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Append (FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, messages, flags, dates, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously append the specified messages to the folder. - /// - /// - /// Appends the specified messages to the folder and returns the UniqueIds assigned to the messages. - /// - /// The UIDs of the appended messages, if available; otherwise an empty array. - /// The formatting options. - /// The list of messages to append to the folder. - /// The message flags to use for each of the messages. - /// The received dates to use for each of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is null. - /// -or- - /// The number of messages, flags, and dates do not match. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> AppendAsync (FormatOptions options, IList messages, IList flags, IList dates, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return AppendAsync (options, messages, flags, dates, true, cancellationToken, progress); - } - - ImapCommand QueueReplace (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken, ITransferProgress progress) - { - var builder = new StringBuilder ($"UID REPLACE {uid} %F "); - var list = new List (); - - list.Add (this); - - if ((flags & SettableFlags) != 0) - builder.AppendFormat ("{0} ", ImapUtils.FormatFlagsList (flags, 0)); - - if (date.HasValue) - builder.AppendFormat ("\"{0}\" ", ImapUtils.FormatInternalDate (date.Value)); - - //if (annotations != null && annotations.Count > 0) { - // ImapUtils.FormatAnnotations (builder, annotations, list, false); - // - // if (builder[builder.Length - 1] != ' ') - // builder.Append (' '); - //} - - builder.Append ("%L\r\n"); - list.Add (message); - - var command = builder.ToString (); - var args = list.ToArray (); - - var ic = new ImapCommand (Engine, cancellationToken, null, options, command, args); - ic.Progress = progress; - - Engine.QueueCommand (ic); - - return ic; - } - - async Task ReplaceAsync (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - CheckState (true, true); - - if ((Engine.Capabilities & ImapCapabilities.Replace) == 0) { - var appended = await AppendAsync (options, message, flags, date, annotations, doAsync, cancellationToken, progress).ConfigureAwait (false); - await ModifyFlagsAsync (new[] { uid }, null, MessageFlags.Deleted, null, "+FLAGS.SILENT", doAsync, cancellationToken).ConfigureAwait (false); - if ((Engine.Capabilities & ImapCapabilities.UidPlus) != 0) - await ExpungeAsync (new[] { uid }, doAsync, cancellationToken).ConfigureAwait (false); - return appended; - } - - if (options.International && (Engine.Capabilities & ImapCapabilities.UTF8Accept) == 0) - throw new NotSupportedException ("The IMAP server does not support the UTF8 extension."); - - var format = options.Clone (); - format.NewLineFormat = NewLineFormat.Dos; - format.EnsureNewLine = true; - - if ((Engine.Capabilities & ImapCapabilities.UTF8Only) == ImapCapabilities.UTF8Only) - format.International = true; - - if (format.International && !Engine.UTF8Enabled) - throw new InvalidOperationException ("The UTF8 extension has not been enabled."); - - var ic = QueueReplace (format, uid, message, flags, date, annotations, cancellationToken, progress); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, this); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("REPLACE", ic); - - var append = (AppendUidResponseCode) GetResponseCode (ic, ImapResponseCodeType.AppendUid); - - if (append != null) - return append.UidSet[0]; - - return null; - } - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueId? Replace (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (options, uid, message, flags, null, null, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task ReplaceAsync (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (options, uid, message, flags, null, null, true, cancellationToken, progress); - } - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueId? Replace (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (options, uid, message, flags, date, null, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The UID of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task ReplaceAsync (FormatOptions options, UniqueId uid, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (options, uid, message, flags, date, null, true, cancellationToken, progress); - } - - ImapCommand QueueReplace (FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, CancellationToken cancellationToken, ITransferProgress progress) - { - var builder = new StringBuilder ($"REPLACE %d %F "); - var list = new List (); - - list.Add (index + 1); - list.Add (this); - - if ((flags & SettableFlags) != 0) - builder.AppendFormat ("{0} ", ImapUtils.FormatFlagsList (flags, 0)); - - if (date.HasValue) - builder.AppendFormat ("\"{0}\" ", ImapUtils.FormatInternalDate (date.Value)); - - //if (annotations != null && annotations.Count > 0) { - // ImapUtils.FormatAnnotations (builder, annotations, list, false); - // - // if (builder[builder.Length - 1] != ' ') - // builder.Append (' '); - //} - - builder.Append ("%L\r\n"); - list.Add (message); - - var command = builder.ToString (); - var args = list.ToArray (); - - var ic = new ImapCommand (Engine, cancellationToken, null, options, command, args); - ic.Progress = progress; - - Engine.QueueCommand (ic); - - return ic; - } - - async Task ReplaceAsync (FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset? date, IList annotations, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - CheckState (true, true); - - if ((Engine.Capabilities & ImapCapabilities.Replace) == 0) { - var uid = await AppendAsync (options, message, flags, date, annotations, doAsync, cancellationToken, progress).ConfigureAwait (false); - await ModifyFlagsAsync (new[] { index }, null, MessageFlags.Deleted, null, "+FLAGS.SILENT", doAsync, cancellationToken).ConfigureAwait (false); - return uid; - } - - if (options.International && (Engine.Capabilities & ImapCapabilities.UTF8Accept) == 0) - throw new NotSupportedException ("The IMAP server does not support the UTF8 extension."); - - var format = options.Clone (); - format.NewLineFormat = NewLineFormat.Dos; - format.EnsureNewLine = true; - - if ((Engine.Capabilities & ImapCapabilities.UTF8Only) == ImapCapabilities.UTF8Only) - format.International = true; - - if (format.International && !Engine.UTF8Enabled) - throw new InvalidOperationException ("The UTF8 extension has not been enabled."); - - var ic = QueueReplace (format, index, message, flags, date, annotations, cancellationToken, progress); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, this); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("REPLACE", ic); - - var append = (AppendUidResponseCode) GetResponseCode (ic, ImapResponseCodeType.AppendUid); - - if (append != null) - return append.UidSet[0]; - - return null; - } - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueId? Replace (FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (options, index, message, flags, null, null, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task ReplaceAsync (FormatOptions options, int index, MimeMessage message, MessageFlags flags = MessageFlags.None, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (options, index, message, flags, null, null, true, cancellationToken, progress); - } - - /// - /// Replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueId? Replace (FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (options, index, message, flags, date, null, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously replace a message in the folder. - /// - /// - /// Replaces the specified message in the folder and returns the UniqueId assigned to the new message. - /// - /// The UID of the new message, if available; otherwise, null. - /// The formatting options. - /// The index of the message to be replaced. - /// The message. - /// The message flags. - /// The received date of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// Internationalized formatting was requested but has not been enabled. - /// - /// - /// The does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task ReplaceAsync (FormatOptions options, int index, MimeMessage message, MessageFlags flags, DateTimeOffset date, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return ReplaceAsync (options, index, message, flags, date, null, true, cancellationToken, progress); - } - - async Task> GetIndexesAsync (IList uids, bool doAsync, CancellationToken cancellationToken) - { - var command = string.Format ("SEARCH UID {0}\r\n", UniqueIdSet.ToString (uids)); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var results = new SearchResults (SortOrder.Ascending); - - if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0) - ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync); - - ic.RegisterUntaggedHandler ("SEARCH", SearchMatchesAsync); - ic.UserData = results; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("SEARCH", ic); - - var indexes = new int[results.UniqueIds.Count]; - for (int i = 0; i < indexes.Length; i++) - indexes[i] = (int) results.UniqueIds[i].Id - 1; - - return indexes; - } - - async Task CopyToAsync (IList uids, IMailFolder destination, bool doAsync, CancellationToken cancellationToken) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - if (!(destination is ImapFolder) || ((ImapFolder) destination).Engine != Engine) - throw new ArgumentException ("The destination folder does not belong to this ImapClient.", nameof (destination)); - - CheckState (true, false); - - if (uids.Count == 0) - return UniqueIdMap.Empty; - - if ((Engine.Capabilities & ImapCapabilities.UidPlus) == 0) { - var indexes = await GetIndexesAsync (uids, doAsync, cancellationToken).ConfigureAwait (false); - await CopyToAsync (indexes, destination, doAsync, cancellationToken).ConfigureAwait (false); - return UniqueIdMap.Empty; - } - - 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, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, destination); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("COPY", ic); - - var copy = (CopyUidResponseCode) GetResponseCode (ic, ImapResponseCodeType.CopyUid); - - if (copy != null) { - if (dest == null) { - dest = copy.DestUidSet; - src = copy.SrcUidSet; - } else { - dest.AddRange (copy.DestUidSet); - src.AddRange (copy.SrcUidSet); - } - } - } - - if (dest == null) - return UniqueIdMap.Empty; - - return new UniqueIdMap (src, dest); - } - - /// - /// Copy the specified messages to the destination folder. - /// - /// - /// Copies the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// does not exist. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueIdMap CopyTo (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - return CopyToAsync (uids, destination, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously copy the specified messages to the destination folder. - /// - /// - /// Copies the specified messages to the destination folder. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// does not exist. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server does not support the UIDPLUS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task CopyToAsync (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - return CopyToAsync (uids, destination, true, cancellationToken); - } - - async Task MoveToAsync (IList uids, IMailFolder destination, bool doAsync, CancellationToken cancellationToken) - { - if ((Engine.Capabilities & ImapCapabilities.Move) == 0) { - var copied = await CopyToAsync (uids, destination, doAsync, cancellationToken).ConfigureAwait (false); - await ModifyFlagsAsync (uids, null, MessageFlags.Deleted, null, "+FLAGS.SILENT", doAsync, cancellationToken).ConfigureAwait (false); - await ExpungeAsync (uids, doAsync, cancellationToken).ConfigureAwait (false); - return copied; - } - - if ((Engine.Capabilities & ImapCapabilities.UidPlus) == 0) { - var indexes = await GetIndexesAsync (uids, doAsync, cancellationToken).ConfigureAwait (false); - await MoveToAsync (indexes, destination, doAsync, cancellationToken).ConfigureAwait (false); - return UniqueIdMap.Empty; - } - - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - if (!(destination is ImapFolder) || ((ImapFolder) destination).Engine != Engine) - throw new ArgumentException ("The destination folder does not belong to this ImapClient.", nameof (destination)); - - CheckState (true, true); - - if (uids.Count == 0) - return UniqueIdMap.Empty; - - 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, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, destination); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("MOVE", ic); - - var copy = (CopyUidResponseCode) GetResponseCode (ic, ImapResponseCodeType.CopyUid); - - if (copy != null) { - if (dest == null) { - dest = copy.DestUidSet; - src = copy.SrcUidSet; - } else { - dest.AddRange (copy.DestUidSet); - src.AddRange (copy.SrcUidSet); - } - } - } - - if (dest == null) - return UniqueIdMap.Empty; - - return new UniqueIdMap (src, dest); - } - - /// - /// Move the specified messages to the destination folder. - /// - /// - /// Moves the specified messages to the destination folder. - /// If the IMAP server supports the MOVE extension (check the - /// property for the flag), then this operation will be atomic. - /// Otherwise, MailKit implements this by first copying the messages to the destination folder, then - /// marking them for deletion in the originating folder, and finally expunging them (see - /// for more information about how a - /// subset of messages are expunged). Since the server could disconnect at any point between those 3 - /// (or more) commands, it is advisable for clients to implement their own logic for moving messages when - /// the IMAP server does not support the MOVE command in order to better handle spontanious server - /// disconnects and other error conditions. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override UniqueIdMap MoveTo (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - return MoveToAsync (uids, destination, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously move the specified messages to the destination folder. - /// - /// - /// Moves the specified messages to the destination folder. - /// If the IMAP server supports the MOVE extension (check the - /// property for the flag), then this operation will be atomic. - /// Otherwise, MailKit implements this by first copying the messages to the destination folder, then - /// marking them for deletion in the originating folder, and finally expunging them (see - /// for more information about how a - /// subset of messages are expunged). Since the server could disconnect at any point between those 3 - /// (or more) commands, it is advisable for clients to implement their own logic for moving messages when - /// the IMAP server does not support the MOVE command in order to better handle spontanious server - /// disconnects and other error conditions. - /// - /// The UID mapping of the messages in the destination folder, if available; otherwise an empty mapping. - /// The UIDs of the messages to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task MoveToAsync (IList uids, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - return MoveToAsync (uids, destination, true, cancellationToken); - } - - async Task CopyToAsync (IList indexes, IMailFolder destination, bool doAsync, CancellationToken cancellationToken) - { - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - if (!(destination is ImapFolder) || ((ImapFolder) destination).Engine != Engine) - throw new ArgumentException ("The destination folder does not belong to this ImapClient.", nameof (destination)); - - CheckState (true, false); - CheckAllowIndexes (); - - if (indexes.Count == 0) - return; - - var set = ImapUtils.FormatIndexSet (indexes); - var command = string.Format ("COPY {0} %F\r\n", set); - var ic = Engine.QueueCommand (cancellationToken, this, command, destination); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, destination); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("COPY", ic); - } - - /// - /// Copy the specified messages to the destination folder. - /// - /// - /// Copies the specified messages to the destination folder. - /// - /// The indexes of the messages to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not currently open. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// does not exist. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void CopyTo (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - CopyToAsync (indexes, destination, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously copy the specified messages to the destination folder. - /// - /// - /// Copies the specified messages to the destination folder. - /// - /// An awaitable task. - /// The indexes of the messages to copy. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not currently open. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// does not exist. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task CopyToAsync (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - return CopyToAsync (indexes, destination, true, cancellationToken); - } - - async Task MoveToAsync (IList indexes, IMailFolder destination, bool doAsync, CancellationToken cancellationToken) - { - if ((Engine.Capabilities & ImapCapabilities.Move) == 0) { - await CopyToAsync (indexes, destination, doAsync, cancellationToken).ConfigureAwait (false); - await ModifyFlagsAsync (indexes, null, MessageFlags.Deleted, null, "+FLAGS.SILENT", doAsync, cancellationToken).ConfigureAwait (false); - return; - } - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - if (!(destination is ImapFolder) || ((ImapFolder) destination).Engine != Engine) - throw new ArgumentException ("The destination folder does not belong to this ImapClient.", nameof (destination)); - - CheckState (true, true); - CheckAllowIndexes (); - - if (indexes.Count == 0) - return; - - var set = ImapUtils.FormatIndexSet (indexes); - var command = string.Format ("MOVE {0} %F\r\n", set); - var ic = Engine.QueueCommand (cancellationToken, this, command, destination); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, destination); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("MOVE", ic); - } - - /// - /// Move the specified messages to the destination folder. - /// - /// - /// If the IMAP server supports the MOVE command, then the MOVE command will be used. Otherwise, - /// the messages will first be copied to the destination folder and then marked as \Deleted in the - /// originating folder. Since the server could disconnect at any point between those 2 operations, it - /// may be advisable to implement your own logic for moving messages in this case in order to better - /// handle spontanious server disconnects and other error conditions. - /// - /// The indexes of the messages to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void MoveTo (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - MoveToAsync (indexes, destination, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously move the specified messages to the destination folder. - /// - /// - /// If the IMAP server supports the MOVE command, then the MOVE command will be used. Otherwise, - /// the messages will first be copied to the destination folder and then marked as \Deleted in the - /// originating folder. Since the server could disconnect at any point between those 2 operations, it - /// may be advisable to implement your own logic for moving messages in this case in order to better - /// handle spontanious server disconnects and other error conditions. - /// - /// An awaitable task. - /// The indexes of the messages to move. - /// The destination folder. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// The destination folder does not belong to the . - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// does not exist. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task MoveToAsync (IList indexes, IMailFolder destination, CancellationToken cancellationToken = default (CancellationToken)) - { - return MoveToAsync (indexes, destination, true, cancellationToken); - } - - #region IEnumerable implementation - - /// - /// Get an enumerator for the messages in the folder. - /// - /// - /// Gets an enumerator for the messages in the folder. - /// - /// The enumerator. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - public override IEnumerator GetEnumerator () - { - CheckState (true, false); - - for (int i = 0; i < Count; i++) - yield return GetMessage (i, CancellationToken.None); - - yield break; - } - - #endregion - - #region Untagged response handlers called by ImapEngine - - internal void OnExists (int count) - { - if (Count == count) - return; - - Count = count; - - OnCountChanged (); - } - - internal void OnExpunge (int index) - { - Count--; - - OnMessageExpunged (new MessageEventArgs (index)); - OnCountChanged (); - } - - internal async Task OnFetchAsync (ImapEngine engine, int index, bool doAsync, CancellationToken cancellationToken) - { - var message = new MessageSummary (this, index); - UniqueId? uid = null; - - await FetchSummaryItemsAsync (engine, message, doAsync, cancellationToken).ConfigureAwait (false); - - if ((message.Fields & MessageSummaryItems.UniqueId) != 0) - uid = message.UniqueId; - - if ((message.Fields & MessageSummaryItems.Flags) != 0) { - var args = new MessageFlagsChangedEventArgs (index, message.Flags.Value, message.Keywords); - args.ModSeq = message.ModSeq; - args.UniqueId = uid; - - OnMessageFlagsChanged (args); - } - - if ((message.Fields & MessageSummaryItems.GMailLabels) != 0) { - var args = new MessageLabelsChangedEventArgs (index, message.GMailLabels); - args.ModSeq = message.ModSeq; - args.UniqueId = uid; - - OnMessageLabelsChanged (args); - } - - if ((message.Fields & MessageSummaryItems.Annotations) != 0) { - var args = new AnnotationsChangedEventArgs (index, message.Annotations); - args.ModSeq = message.ModSeq; - args.UniqueId = uid; - - OnAnnotationsChanged (args); - } - - if ((message.Fields & MessageSummaryItems.ModSeq) != 0) { - var args = new ModSeqChangedEventArgs (index, message.ModSeq.Value); - args.UniqueId = uid; - - OnModSeqChanged (args); - } - - if (message.Fields != MessageSummaryItems.None) - OnMessageSummaryFetched (message); - } - - internal void OnRecent (int count) - { - if (Recent == count) - return; - - Recent = count; - - OnRecentChanged (); - } - - internal async Task OnVanishedAsync (ImapEngine engine, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - UniqueIdSet vanished; - bool earlier = false; - - if (token.Type == ImapTokenType.OpenParen) { - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "VANISHED", token); - - var atom = (string) token.Value; - - if (atom == "EARLIER") - earlier = true; - } while (true); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - vanished = ImapEngine.ParseUidSet (token, UidValidity, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "VANISHED", token); - - OnMessagesVanished (new MessagesVanishedEventArgs (vanished, earlier)); - - if (!earlier) { - Count -= vanished.Count; - - OnCountChanged (); - } - } - - internal void UpdateAttributes (FolderAttributes attrs) - { - var unsubscribed = false; - var subscribed = false; - - if ((attrs & FolderAttributes.Subscribed) == 0) - unsubscribed = (Attributes & FolderAttributes.Subscribed) != 0; - else - subscribed = (Attributes & FolderAttributes.Subscribed) == 0; - - var deleted = ((attrs & FolderAttributes.NonExistent) != 0) && - (Attributes & FolderAttributes.NonExistent) == 0; - - Attributes = attrs; - - if (unsubscribed) - OnUnsubscribed (); - - if (subscribed) - OnSubscribed (); - - if (deleted) - OnDeleted (); - } - - internal void UpdateAcceptedFlags (MessageFlags flags) - { - AcceptedFlags = flags; - } - - internal void UpdatePermanentFlags (MessageFlags flags) - { - PermanentFlags = flags; - } - - internal void UpdateIsNamespace (bool value) - { - IsNamespace = value; - } - - internal void UpdateUnread (int count) - { - if (Unread == count) - return; - - Unread = count; - - OnUnreadChanged (); - } - - internal void UpdateUidNext (UniqueId uid) - { - if (UidNext.HasValue && UidNext.Value == uid) - return; - - UidNext = uid; - - OnUidNextChanged (); - } - - internal void UpdateAppendLimit (uint? limit) - { - AppendLimit = limit; - } - - internal void UpdateSize (ulong? size) - { - if (Size == size) - return; - - Size = size; - - OnSizeChanged (); - } - - internal void UpdateId (string id) - { - if (Id == id) - return; - - Id = id; - - OnIdChanged (); - } - - internal void UpdateHighestModSeq (ulong modseq) - { - if (HighestModSeq == modseq) - return; - - HighestModSeq = modseq; - - OnHighestModSeqChanged (); - } - - internal void UpdateUidValidity (uint validity) - { - if (UidValidity == validity) - return; - - UidValidity = validity; - - OnUidValidityChanged (); - } - - internal void OnRenamed (ImapFolderConstructorArgs args) - { - var oldFullName = FullName; - - InitializeProperties (args); - - OnRenamed (oldFullName, FullName); - } - - #endregion - - #endregion - } -} diff --git a/src/MailKit/Net/Imap/ImapFolderAnnotations.cs b/src/MailKit/Net/Imap/ImapFolderAnnotations.cs deleted file mode 100644 index cf20979..0000000 --- a/src/MailKit/Net/Imap/ImapFolderAnnotations.cs +++ /dev/null @@ -1,567 +0,0 @@ -// -// ImapFolderAnnotations.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MailKit.Net.Imap -{ - public partial class ImapFolder - { - async Task> StoreAsync (IList uids, ulong? modseq, IList annotations, bool doAsync, CancellationToken cancellationToken) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (modseq.HasValue && !supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - if (annotations == null) - throw new ArgumentNullException (nameof (annotations)); - - CheckState (true, true); - - if (AnnotationAccess == AnnotationAccess.None) - throw new NotSupportedException ("The ImapFolder does not support annotations."); - - if (uids.Count == 0 || annotations.Count == 0) - return new UniqueId[0]; - - var builder = new StringBuilder ("UID STORE %s "); - var values = new List (); - UniqueIdSet unmodified = null; - - if (modseq.HasValue) - builder.AppendFormat (CultureInfo.InvariantCulture, "(UNCHANGEDSINCE {0}) ", modseq.Value); - - ImapUtils.FormatAnnotations (builder, annotations, values, true); - builder.Append ("\r\n"); - - var command = builder.ToString (); - var args = values.ToArray (); - - foreach (var ic in Engine.QueueCommands (cancellationToken, this, command, uids, args)) { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("STORE", ic); - - ProcessUnmodified (ic, ref unmodified, modseq); - } - - if (unmodified == null) - return new UniqueId[0]; - - return unmodified; - } - - /// - /// Store the annotations for the specified messages. - /// - /// - /// Stores the annotations for the specified messages. - /// - /// The UIDs of the messages. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Store (IList uids, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - StoreAsync (uids, null, annotations, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously store the annotations for the specified messages. - /// - /// - /// Asynchronously stores the annotations for the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task StoreAsync (IList uids, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - return StoreAsync (uids, null, annotations, true, cancellationToken); - } - - /// - /// Store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// -or- - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Store (IList uids, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - return StoreAsync (uids, modseq, annotations, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// -or- - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> StoreAsync (IList uids, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - return StoreAsync (uids, modseq, annotations, true, cancellationToken); - } - - async Task> StoreAsync (IList indexes, ulong? modseq, IList annotations, bool doAsync, CancellationToken cancellationToken) - { - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (modseq.HasValue && !supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - if (annotations == null) - throw new ArgumentNullException (nameof (annotations)); - - CheckState (true, true); - - if (AnnotationAccess == AnnotationAccess.None) - throw new NotSupportedException ("The ImapFolder does not support annotations."); - - if (indexes.Count == 0 || annotations.Count == 0) - return new int[0]; - - var set = ImapUtils.FormatIndexSet (indexes); - var builder = new StringBuilder ("STORE "); - var values = new List (); - - builder.AppendFormat ("{0} ", set); - - if (modseq.HasValue) - builder.AppendFormat (CultureInfo.InvariantCulture, "(UNCHANGEDSINCE {0}) ", modseq.Value); - - ImapUtils.FormatAnnotations (builder, annotations, values, true); - builder.Append ("\r\n"); - - var command = builder.ToString (); - var args = values.ToArray (); - - var ic = Engine.QueueCommand (cancellationToken, this, command, args); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("STORE", ic); - - return GetUnmodified (ic, modseq); - } - - /// - /// Store the annotations for the specified messages. - /// - /// - /// Stores the annotations for the specified messages. - /// - /// The indexes of the messages. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void Store (IList indexes, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - StoreAsync (indexes, null, annotations, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously store the annotations for the specified messages. - /// - /// - /// Asynchronously stores the annotations for the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task StoreAsync (IList indexes, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - return StoreAsync (indexes, null, annotations, true, cancellationToken); - } - - /// - /// Store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Stores the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// -or- - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Store (IList indexes, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - return StoreAsync (indexes, modseq, annotations, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously store the annotations for the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Asynchronously stores the annotations for the specified messages only if their mod-sequence value is less than the specified value.s - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The annotations to store. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// Cannot store annotations without any properties defined. - /// - /// - /// The does not support annotations. - /// -or- - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> StoreAsync (IList indexes, ulong modseq, IList annotations, CancellationToken cancellationToken = default (CancellationToken)) - { - return StoreAsync (indexes, modseq, annotations, true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Imap/ImapFolderConstructorArgs.cs b/src/MailKit/Net/Imap/ImapFolderConstructorArgs.cs deleted file mode 100644 index c9ba6aa..0000000 --- a/src/MailKit/Net/Imap/ImapFolderConstructorArgs.cs +++ /dev/null @@ -1,113 +0,0 @@ -// -// ImapFolderInfo.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Net.Imap { - /// - /// Constructor arguments for . - /// - /// - /// Constructor arguments for . - /// - public sealed class ImapFolderConstructorArgs - { - internal readonly string EncodedName; - internal readonly ImapEngine Engine; - - /// - /// Initializes a new instance of the class. - /// - /// The IMAP command engine. - /// The encoded name. - /// The attributes. - /// The directory separator. - internal ImapFolderConstructorArgs (ImapEngine engine, string encodedName, FolderAttributes attributes, char delim) : this () - { - FullName = engine.DecodeMailboxName (encodedName); - Name = GetBaseName (FullName, delim); - DirectorySeparator = delim; - EncodedName = encodedName; - Attributes = attributes; - Engine = engine; - } - - ImapFolderConstructorArgs () - { - } - - /// - /// Get the folder attributes. - /// - /// - /// Gets the folder attributes. - /// - /// The folder attributes. - public FolderAttributes Attributes { - get; private set; - } - - /// - /// Get the directory separator. - /// - /// - /// Gets the directory separator. - /// - /// The directory separator. - public char DirectorySeparator { - get; private set; - } - - /// - /// Get the full name of the folder. - /// - /// - /// This is the equivalent of the full path of a file on a file system. - /// - /// The full name of the folder. - public string FullName { - get; private set; - } - - /// - /// Get the name of the folder. - /// - /// - /// This is the equivalent of the file name of a file on the file system. - /// - /// The name of the folder. - public string Name { - get; private set; - } - - static string GetBaseName (string fullName, char delim) - { - var names = fullName.Split (new [] { delim }, StringSplitOptions.RemoveEmptyEntries); - - return names.Length > 0 ? names[names.Length - 1] : fullName; - } - } -} diff --git a/src/MailKit/Net/Imap/ImapFolderFetch.cs b/src/MailKit/Net/Imap/ImapFolderFetch.cs deleted file mode 100644 index d08e9c8..0000000 --- a/src/MailKit/Net/Imap/ImapFolderFetch.cs +++ /dev/null @@ -1,6770 +0,0 @@ -// -// ImapFolderFetch.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -using MimeKit; -using MimeKit.IO; -using MimeKit.Text; -using MimeKit.Utils; - -using MailKit.Search; - -namespace MailKit.Net.Imap -{ - public partial class ImapFolder - { - internal static readonly HashSet EmptyHeaderFields = new HashSet (); - const int PreviewHtmlLength = 16 * 1024; - const int PreviewTextLength = 512; - - class FetchSummaryContext - { - public readonly List Messages; - - public FetchSummaryContext () - { - Messages = new List (); - } - - int BinarySearch (int index, bool insert) - { - int min = 0, max = Messages.Count; - - if (max == 0) - return insert ? 0 : -1; - - do { - int i = min + ((max - min) / 2); - - if (index == Messages[i].Index) - return i; - - if (index > Messages[i].Index) { - min = i + 1; - } else { - max = i; - } - } while (min < max); - - return insert ? min : -1; - } - - public void Add (int index, MessageSummary message) - { - int i = BinarySearch (index, true); - - if (i < Messages.Count) - Messages.Insert (i, message); - else - Messages.Add (message); - } - - public bool TryGetValue (int index, out MessageSummary message) - { - int i; - - if ((i = BinarySearch (index, false)) == -1) { - message = null; - return false; - } - - message = (MessageSummary) Messages[i]; - - return true; - } - - public void OnMessageExpunged (object sender, MessageEventArgs args) - { - int index = BinarySearch (args.Index, true); - - if (index >= Messages.Count) - return; - - if (Messages[index].Index == args.Index) - Messages.RemoveAt (index); - - for (int i = index; i < Messages.Count; i++) { - var message = (MessageSummary) Messages[i]; - message.Index--; - } - } - } - - static async Task ReadLiteralDataAsync (ImapEngine engine, bool doAsync, CancellationToken cancellationToken) - { - var buf = new byte[4096]; - int nread; - - do { - if (doAsync) - nread = await engine.Stream.ReadAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false); - else - nread = engine.Stream.Read (buf, 0, buf.Length, cancellationToken); - } while (nread > 0); - } - - static async Task SkipParenthesizedList (ImapEngine engine, bool doAsync, CancellationToken cancellationToken) - { - do { - var token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Eoln) - return; - - // token is safe to read, so pop it off the queue - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - if (token.Type == ImapTokenType.OpenParen) { - // skip the inner parenthesized list - await SkipParenthesizedList (engine, doAsync, cancellationToken).ConfigureAwait (false); - } - } while (true); - } - - async Task FetchSummaryItemsAsync (ImapEngine engine, MessageSummary message, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen || token.Type == ImapTokenType.Eoln) - break; - - bool parenthesized = false; - if (engine.QuirksMode == ImapQuirksMode.Domino && token.Type == ImapTokenType.OpenParen) { - // Note: Lotus Domino IMAP will (sometimes?) encapsulate the `ENVELOPE` segment of the - // response within an extra set of parenthesis. - // - // See https://github.com/jstedfast/MailKit/issues/943 for details. - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - parenthesized = true; - } - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - - var atom = (string) token.Value; - string format; - ulong value64; - uint value; - int idx; - - switch (atom.ToUpperInvariant ()) { - case "INTERNALDATE": - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - switch (token.Type) { - case ImapTokenType.QString: - case ImapTokenType.Atom: - message.InternalDate = ImapUtils.ParseInternalDate ((string) token.Value); - break; - case ImapTokenType.Nil: - message.InternalDate = null; - break; - default: - throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - } - - message.Fields |= MessageSummaryItems.InternalDate; - break; - case "RFC822.SIZE": - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - message.Size = ImapEngine.ParseNumber (token, false, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - message.Fields |= MessageSummaryItems.Size; - break; - case "BODYSTRUCTURE": - format = string.Format (ImapEngine.GenericItemSyntaxErrorFormat, "BODYSTRUCTURE", "{0}"); - message.Body = await ImapUtils.ParseBodyAsync (engine, format, string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - message.Fields |= MessageSummaryItems.BodyStructure; - break; - case "BODY": - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - format = ImapEngine.FetchBodySyntaxErrorFormat; - - if (token.Type == ImapTokenType.OpenBracket) { - var referencesField = false; - var headerFields = false; - - // consume the '[' - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenBracket, format, token); - - // References and/or other headers were requested... - - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseBracket) - break; - - if (token.Type == ImapTokenType.OpenParen) { - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - // the header field names will generally be atoms or qstrings but may also be literals - engine.Stream.UngetToken (token); - - var field = await ImapUtils.ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - if (headerFields && !referencesField && field.Equals ("REFERENCES", StringComparison.OrdinalIgnoreCase)) - referencesField = true; - } while (true); - } else { - ImapEngine.AssertToken (token, ImapTokenType.Atom, format, token); - - atom = (string) token.Value; - - headerFields = atom.Equals ("HEADER.FIELDS", StringComparison.OrdinalIgnoreCase); - - if (!headerFields && atom.Equals ("HEADER", StringComparison.OrdinalIgnoreCase)) { - // if we're fetching *all* headers, then it will include the References header (if it exists) - referencesField = true; - } - } - } while (true); - - ImapEngine.AssertToken (token, ImapTokenType.CloseBracket, format, token); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.Literal, format, token); - - try { - message.Headers = await engine.ParseHeadersAsync (engine.Stream, doAsync, cancellationToken).ConfigureAwait (false); - } catch (FormatException) { - message.Headers = new HeaderList (); - } - - // consume any remaining literal data... (typically extra blank lines) - await ReadLiteralDataAsync (engine, doAsync, cancellationToken).ConfigureAwait (false); - - message.References = new MessageIdList (); - - if ((idx = message.Headers.IndexOf (HeaderId.References)) != -1) { - var references = message.Headers[idx]; - var rawValue = references.RawValue; - - foreach (var msgid in MimeUtils.EnumerateReferences (rawValue, 0, rawValue.Length)) - message.References.Add (msgid); - } - - message.Fields |= MessageSummaryItems.Headers; - - if (referencesField) - message.Fields |= MessageSummaryItems.References; - } else { - message.Body = await ImapUtils.ParseBodyAsync (engine, format, string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - message.Fields |= MessageSummaryItems.Body; - } - break; - case "ENVELOPE": - message.Envelope = await ImapUtils.ParseEnvelopeAsync (engine, doAsync, cancellationToken).ConfigureAwait (false); - message.Fields |= MessageSummaryItems.Envelope; - break; - case "FLAGS": - message.Flags = await ImapUtils.ParseFlagsListAsync (engine, atom, message.Keywords, doAsync, cancellationToken).ConfigureAwait (false); - message.Fields |= MessageSummaryItems.Flags; - break; - case "MODSEQ": - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - value64 = ImapEngine.ParseNumber64 (token, false, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - message.Fields |= MessageSummaryItems.ModSeq; - message.ModSeq = value64; - - if (value64 > HighestModSeq) - UpdateHighestModSeq (value64); - break; - case "UID": - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - value = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - message.UniqueId = new UniqueId (UidValidity, value); - message.Fields |= MessageSummaryItems.UniqueId; - break; - case "EMAILID": - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - message.Fields |= MessageSummaryItems.EmailId; - message.EmailId = (string) token.Value; - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - break; - case "THREADID": - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenParen) { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - message.Fields |= MessageSummaryItems.ThreadId; - message.ThreadId = (string) token.Value; - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - } else { - ImapEngine.AssertToken (token, ImapTokenType.Nil, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - message.Fields |= MessageSummaryItems.ThreadId; - message.ThreadId = null; - } - break; - case "X-GM-MSGID": - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - value64 = ImapEngine.ParseNumber64 (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - message.Fields |= MessageSummaryItems.GMailMessageId; - message.GMailMessageId = value64; - break; - case "X-GM-THRID": - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - value64 = ImapEngine.ParseNumber64 (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - message.Fields |= MessageSummaryItems.GMailThreadId; - message.GMailThreadId = value64; - break; - case "X-GM-LABELS": - message.GMailLabels = await ImapUtils.ParseLabelsListAsync (engine, doAsync, cancellationToken).ConfigureAwait (false); - message.Fields |= MessageSummaryItems.GMailLabels; - break; - case "ANNOTATION": - message.Annotations = await ImapUtils.ParseAnnotationsAsync (engine, doAsync, cancellationToken).ConfigureAwait (false); - message.Fields |= MessageSummaryItems.Annotations; - break; - default: - // 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 (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenParen) - await SkipParenthesizedList (engine, doAsync, cancellationToken).ConfigureAwait (false); - break; - } - - if (parenthesized) { - // Note: This is the second half of the Lotus Domino IMAP server work-around. - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - } - } while (true); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - } - - async Task FetchSummaryItemsAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var ctx = (FetchSummaryContext) ic.UserData; - MessageSummary message; - - if (!ctx.TryGetValue (index, out message)) { - message = new MessageSummary (this, index); - ctx.Add (index, message); - } - - await FetchSummaryItemsAsync (engine, message, doAsync, ic.CancellationToken).ConfigureAwait (false); - - OnMessageSummaryFetched (message); - } - - internal static string FormatSummaryItems (ImapEngine engine, ref MessageSummaryItems items, HashSet headers, out bool previewText, bool isNotify = false) - { - if ((items & MessageSummaryItems.PreviewText) != 0) { - // if the user wants the preview text, we will also need the UIDs and BODYSTRUCTUREs - // so that we can request a preview of the body text in subsequent FETCH requests. - items |= MessageSummaryItems.BodyStructure | MessageSummaryItems.UniqueId; - previewText = true; - } else { - previewText = false; - } - - if ((items & MessageSummaryItems.BodyStructure) != 0 && (items & MessageSummaryItems.Body) != 0) { - // don't query both the BODY and BODYSTRUCTURE, that's just dumb... - items &= ~MessageSummaryItems.Body; - } - - if (engine.QuirksMode != ImapQuirksMode.GMail && !isNotify) { - // first, eliminate the aliases... - var alias = items & ~MessageSummaryItems.PreviewText; - - if (alias == MessageSummaryItems.All) - return "ALL"; - - if (alias == MessageSummaryItems.Full) - return "FULL"; - - if (alias == MessageSummaryItems.Fast) - return "FAST"; - } - - var tokens = new List (); - - // now add on any additional summary items... - if ((items & MessageSummaryItems.UniqueId) != 0) - tokens.Add ("UID"); - if ((items & MessageSummaryItems.Flags) != 0) - tokens.Add ("FLAGS"); - if ((items & MessageSummaryItems.InternalDate) != 0) - tokens.Add ("INTERNALDATE"); - if ((items & MessageSummaryItems.Size) != 0) - tokens.Add ("RFC822.SIZE"); - if ((items & MessageSummaryItems.Envelope) != 0) - tokens.Add ("ENVELOPE"); - if ((items & MessageSummaryItems.BodyStructure) != 0) - tokens.Add ("BODYSTRUCTURE"); - if ((items & MessageSummaryItems.Body) != 0) - tokens.Add ("BODY"); - - if ((engine.Capabilities & ImapCapabilities.CondStore) != 0) { - if ((items & MessageSummaryItems.ModSeq) != 0) - tokens.Add ("MODSEQ"); - } - - if ((engine.Capabilities & ImapCapabilities.Annotate) != 0) { - if ((items & MessageSummaryItems.Annotations) != 0) - tokens.Add ("ANNOTATION (/* (value size))"); - } - - if ((engine.Capabilities & ImapCapabilities.ObjectID) != 0) { - if ((items & MessageSummaryItems.EmailId) != 0) - tokens.Add ("EMAILID"); - if ((items & MessageSummaryItems.ThreadId) != 0) - tokens.Add ("THREADID"); - } - - if ((engine.Capabilities & ImapCapabilities.GMailExt1) != 0) { - // now for the GMail extension items - if ((items & MessageSummaryItems.GMailMessageId) != 0) - tokens.Add ("X-GM-MSGID"); - if ((items & MessageSummaryItems.GMailThreadId) != 0) - tokens.Add ("X-GM-THRID"); - if ((items & MessageSummaryItems.GMailLabels) != 0) - tokens.Add ("X-GM-LABELS"); - } - - if ((items & MessageSummaryItems.Headers) != 0) { - tokens.Add ("BODY.PEEK[HEADER]"); - } else if ((items & MessageSummaryItems.References) != 0 || headers.Count > 0) { - var headerFields = new StringBuilder ("BODY.PEEK[HEADER.FIELDS ("); - var references = false; - - foreach (var header in headers) { - if (header.Equals ("REFERENCES", StringComparison.OrdinalIgnoreCase)) - references = true; - - headerFields.Append (header); - headerFields.Append (' '); - } - - if ((items & MessageSummaryItems.References) != 0 && !references) - headerFields.Append ("REFERENCES "); - - headerFields[headerFields.Length - 1] = ')'; - headerFields.Append (']'); - - tokens.Add (headerFields.ToString ()); - } - - if (tokens.Count == 1 && !isNotify) - return tokens[0]; - - return string.Format ("({0})", string.Join (" ", tokens)); - } - - string FormatSummaryItems (ref MessageSummaryItems items, HashSet headers, out bool previewText) - { - return FormatSummaryItems (Engine, ref items, headers, out previewText); - } - - static IList AsReadOnly (ICollection collection) - { - var array = new IMessageSummary[collection.Count]; - - collection.CopyTo (array, 0); - - return new ReadOnlyCollection (array); - } - - class FetchPreviewTextContext : FetchStreamContextBase - { - static readonly PlainTextPreviewer textPreviewer = new PlainTextPreviewer (); - static readonly HtmlTextPreviewer htmlPreviewer = new HtmlTextPreviewer (); - - readonly FetchSummaryContext ctx; - readonly ImapFolder folder; - - public FetchPreviewTextContext (ImapFolder folder, FetchSummaryContext ctx) : base (null) - { - this.folder = folder; - this.ctx = ctx; - } - - public override Task AddAsync (Section section, bool doAsync, CancellationToken cancellationToken) - { - MessageSummary message; - - if (!ctx.TryGetValue (section.Index, out message)) - return Complete; - - var body = message.TextBody; - TextPreviewer previewer; - - if (body == null) { - previewer = htmlPreviewer; - body = message.HtmlBody; - } else { - previewer = textPreviewer; - } - - if (body == null) - return Complete; - - var charset = body.ContentType.Charset ?? "utf-8"; - ContentEncoding encoding; - - if (!string.IsNullOrEmpty (body.ContentTransferEncoding)) - MimeUtils.TryParse (body.ContentTransferEncoding, out encoding); - else - encoding = ContentEncoding.Default; - - using (var memory = new MemoryStream ()) { - var content = new MimeContent (section.Stream, encoding); - - content.DecodeTo (memory); - memory.Position = 0; - - try { - message.PreviewText = previewer.GetPreviewText (memory, charset); - } catch (DecoderFallbackException) { - memory.Position = 0; - - message.PreviewText = previewer.GetPreviewText (memory, ImapEngine.Latin1); - } - - message.Fields |= MessageSummaryItems.PreviewText; - folder.OnMessageSummaryFetched (message); - } - - return Complete; - } - - public override Task SetUniqueIdAsync (int index, UniqueId uid, bool doAsync, CancellationToken cancellationToken) - { - return Complete; - } - } - - async Task FetchPreviewTextAsync (FetchSummaryContext sctx, Dictionary bodies, int octets, bool doAsync, CancellationToken cancellationToken) - { - foreach (var pair in bodies) { - var uids = pair.Value; - string specifier; - - if (!string.IsNullOrEmpty (pair.Key)) - specifier = pair.Key; - else - specifier = "TEXT"; - - // TODO: if the IMAP server supports the CONVERT extension, we could possibly use the - // CONVERT command instead to decode *and* convert (html) into utf-8 plain text. - // - // e.g. "UID CONVERT {0} (\"text/plain\" (\"charset\" \"utf-8\")) BINARY[{1}]<0.{2}>\r\n" - // - // This would allow us to more accurately fetch X number of characters because we wouldn't - // need to guestimate accounting for base64/quoted-printable decoding. - - var command = string.Format (CultureInfo.InvariantCulture, "UID FETCH {0} (BODY.PEEK[{1}]<0.{2}>)\r\n", uids, specifier, octets); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchPreviewTextContext (this, sctx); - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - } finally { - ctx.Dispose (); - } - } - } - - async Task GetPreviewTextAsync (FetchSummaryContext sctx, bool doAsync, CancellationToken cancellationToken) - { - var textBodies = new Dictionary (); - var htmlBodies = new Dictionary (); - - foreach (var item in sctx.Messages) { - Dictionary bodies; - var message = (MessageSummary) item; - var body = message.TextBody; - UniqueIdSet uids; - - if (body == null) { - body = message.HtmlBody; - bodies = htmlBodies; - } else { - bodies = textBodies; - } - - if (body == null) { - message.Fields |= MessageSummaryItems.PreviewText; - message.PreviewText = string.Empty; - OnMessageSummaryFetched (message); - continue; - } - - if (!bodies.TryGetValue (body.PartSpecifier, out uids)) { - uids = new UniqueIdSet (SortOrder.Ascending); - bodies.Add (body.PartSpecifier, uids); - } - - uids.Add (message.UniqueId); - } - - MessageExpunged += sctx.OnMessageExpunged; - - try { - await FetchPreviewTextAsync (sctx, textBodies, PreviewTextLength, doAsync, cancellationToken).ConfigureAwait (false); - await FetchPreviewTextAsync (sctx, htmlBodies, PreviewHtmlLength, doAsync, cancellationToken).ConfigureAwait (false); - } finally { - MessageExpunged -= sctx.OnMessageExpunged; - } - } - - async Task> FetchAsync (IList uids, MessageSummaryItems items, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (items == MessageSummaryItems.None) - throw new ArgumentOutOfRangeException (nameof (items)); - - CheckState (true, false); - - if (uids.Count == 0) - return new IMessageSummary[0]; - - var query = FormatSummaryItems (ref items, EmptyHeaderFields, out previewText); - var command = string.Format ("UID FETCH %s {0}\r\n", query); - var ctx = new FetchSummaryContext (); - - MessageExpunged += ctx.OnMessageExpunged; - - try { - foreach (var ic in Engine.CreateCommands (cancellationToken, this, command, uids)) { - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - } - } finally { - MessageExpunged -= ctx.OnMessageExpunged; - } - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (IList uids, MessageSummaryItems items, IEnumerable headers, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - var headerFields = ImapUtils.GetUniqueHeaders (headers); - - CheckState (true, false); - - if (uids.Count == 0) - return new IMessageSummary[0]; - - var query = FormatSummaryItems (ref items, headerFields, out previewText); - var command = string.Format ("UID FETCH %s {0}\r\n", query); - var ctx = new FetchSummaryContext (); - - MessageExpunged += ctx.OnMessageExpunged; - - try { - foreach (var ic in Engine.CreateCommands (cancellationToken, this, command, uids)) { - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - } - } finally { - MessageExpunged -= ctx.OnMessageExpunged; - } - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (items == MessageSummaryItems.None) - throw new ArgumentOutOfRangeException (nameof (items)); - - if (!supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - CheckState (true, false); - - if (uids.Count == 0) - return new IMessageSummary[0]; - - var query = FormatSummaryItems (ref items, EmptyHeaderFields, out previewText); - var vanished = Engine.QResyncEnabled ? " VANISHED" : string.Empty; - var modseqValue = modseq.ToString (CultureInfo.InvariantCulture); - var command = string.Format ("UID FETCH %s {0} (CHANGEDSINCE {1}{2})\r\n", query, modseqValue, vanished); - var ctx = new FetchSummaryContext (); - - MessageExpunged += ctx.OnMessageExpunged; - - try { - foreach (var ic in Engine.CreateCommands (cancellationToken, this, command, uids)) { - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - } - } finally { - MessageExpunged -= ctx.OnMessageExpunged; - } - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - var headerFields = ImapUtils.GetUniqueHeaders (headers); - - if (!supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - CheckState (true, false); - - if (uids.Count == 0) - return new IMessageSummary[0]; - - var query = FormatSummaryItems (ref items, headerFields, out previewText); - var vanished = Engine.QResyncEnabled ? " VANISHED" : string.Empty; - var modseqValue = modseq.ToString (CultureInfo.InvariantCulture); - var command = string.Format ("UID FETCH %s {0} (CHANGEDSINCE {1}{2})\r\n", query, modseqValue, vanished); - var ctx = new FetchSummaryContext (); - - MessageExpunged += ctx.OnMessageExpunged; - - try { - foreach (var ic in Engine.CreateCommands (cancellationToken, this, command, uids)) { - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - } - } finally { - MessageExpunged -= ctx.OnMessageExpunged; - } - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - /// - /// Fetches the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// - /// - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not currently open. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList uids, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, items, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// - /// - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not currently open. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList uids, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, items, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return Fetch (uids, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Asynchronously fetches the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, items, headers, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the specified message UIDs. - /// - /// - /// Fetches the message summaries for the specified message UIDs. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList uids, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, items, headers, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the IMAP server supports the QRESYNC extension and the application has - /// enabled this feature via , - /// then this method will emit events for messages - /// that have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, modseq, items, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the IMAP server supports the QRESYNC extension and the application has - /// enabled this feature via , - /// then this method will emit events for messages - /// that have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, modseq, items, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the IMAP server supports the QRESYNC extension and the application has - /// enabled this feature via , - /// then this method will emit events for messages - /// that have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return Fetch (uids, modseq, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the IMAP server supports the QRESYNC extension and the application has - /// enabled this feature via , - /// then this method will emit events for messages - /// that have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, modseq, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the IMAP server supports the QRESYNC extension and the application has - /// enabled this feature via , - /// then this method will emit events for messages - /// that have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, modseq, items, headers, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the specified message UIDs that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message UIDs that - /// have a higher mod-sequence value than the one specified. - /// If the IMAP server supports the QRESYNC extension and the application has - /// enabled this feature via , - /// then this method will emit events for messages - /// that have vanished since the specified mod-sequence value. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The UIDs. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList uids, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (uids, modseq, items, headers, true, cancellationToken); - } - - async Task> FetchAsync (IList indexes, MessageSummaryItems items, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (items == MessageSummaryItems.None) - throw new ArgumentOutOfRangeException (nameof (items)); - - CheckState (true, false); - CheckAllowIndexes (); - - if (indexes.Count == 0) - return new IMessageSummary[0]; - - var set = ImapUtils.FormatIndexSet (indexes); - var query = FormatSummaryItems (ref items, EmptyHeaderFields, out previewText); - var command = string.Format ("FETCH {0} {1}\r\n", set, query); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchSummaryContext (); - - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (IList indexes, MessageSummaryItems items, IEnumerable headers, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - var headerFields = ImapUtils.GetUniqueHeaders (headers); - - CheckState (true, false); - CheckAllowIndexes (); - - if (indexes.Count == 0) - return new IMessageSummary[0]; - - var set = ImapUtils.FormatIndexSet (indexes); - var query = FormatSummaryItems (ref items, headerFields, out previewText); - var command = string.Format ("FETCH {0} {1}\r\n", set, query); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchSummaryContext (); - - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (items == MessageSummaryItems.None) - throw new ArgumentOutOfRangeException (nameof (items)); - - if (!supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - CheckState (true, false); - CheckAllowIndexes (); - - if (indexes.Count == 0) - return new IMessageSummary[0]; - - var set = ImapUtils.FormatIndexSet (indexes); - var query = FormatSummaryItems (ref items, EmptyHeaderFields, out previewText); - var modseqValue = modseq.ToString (CultureInfo.InvariantCulture); - var command = string.Format ("FETCH {0} {1} (CHANGEDSINCE {2})\r\n", set, query, modseqValue); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchSummaryContext (); - - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - var headerFields = ImapUtils.GetUniqueHeaders (headers); - - if (!supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - CheckState (true, false); - CheckAllowIndexes (); - - if (indexes.Count == 0) - return new IMessageSummary[0]; - - var set = ImapUtils.FormatIndexSet (indexes); - var query = FormatSummaryItems (ref items, headerFields, out previewText); - var modseqValue = modseq.ToString (CultureInfo.InvariantCulture); - var command = string.Format ("FETCH {0} {1} (CHANGEDSINCE {2})\r\n", set, query, modseqValue); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchSummaryContext (); - - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - /// - /// Fetches the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList indexes, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, items, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList indexes, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, items, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return Fetch (indexes, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Asynchronously fetches the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, items, headers, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the specified message indexes. - /// - /// - /// Fetches the message summaries for the specified message indexes. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList indexes, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, items, headers, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, modseq, items, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, modseq, items, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return Fetch (indexes, modseq, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Asynchronously fetches the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, modseq, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Fetches the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, modseq, items, headers, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the specified message indexes that have a - /// higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the specified message indexes that - /// have a higher mod-sequence value than the one specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The indexes. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (IList indexes, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (indexes, modseq, items, headers, true, cancellationToken); - } - - static string GetFetchRange (int min, int max) - { - var minValue = (min + 1).ToString (CultureInfo.InvariantCulture); - - if (min == max) - return minValue; - - var maxValue = max != -1 ? (max + 1).ToString (CultureInfo.InvariantCulture) : "*"; - - return string.Format (CultureInfo.InvariantCulture, "{0}:{1}", minValue, maxValue); - } - - async Task> FetchAsync (int min, int max, MessageSummaryItems items, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (min < 0) - throw new ArgumentOutOfRangeException (nameof (min)); - - if (max != -1 && max < min) - throw new ArgumentOutOfRangeException (nameof (max)); - - if (items == MessageSummaryItems.None) - throw new ArgumentOutOfRangeException (nameof (items)); - - CheckState (true, false); - CheckAllowIndexes (); - - if (min == Count) - return new IMessageSummary[0]; - - var query = FormatSummaryItems (ref items, EmptyHeaderFields, out previewText); - var command = string.Format ("FETCH {0} {1}\r\n", GetFetchRange (min, max), query); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchSummaryContext (); - - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (int min, int max, MessageSummaryItems items, IEnumerable headers, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (min < 0) - throw new ArgumentOutOfRangeException (nameof (min)); - - if (max != -1 && max < min) - throw new ArgumentOutOfRangeException (nameof (max)); - - var headerFields = ImapUtils.GetUniqueHeaders (headers); - - CheckState (true, false); - CheckAllowIndexes (); - - if (min == Count) - return new IMessageSummary[0]; - - var query = FormatSummaryItems (ref items, headerFields, out previewText); - var command = string.Format ("FETCH {0} {1}\r\n", GetFetchRange (min, max), query); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchSummaryContext (); - - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (min < 0) - throw new ArgumentOutOfRangeException (nameof (min)); - - if (max != -1 && max < min) - throw new ArgumentOutOfRangeException (nameof (max)); - - if (items == MessageSummaryItems.None) - throw new ArgumentOutOfRangeException (nameof (items)); - - if (!supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - CheckState (true, false); - CheckAllowIndexes (); - - var query = FormatSummaryItems (ref items, EmptyHeaderFields, out previewText); - var modseqValue = modseq.ToString (CultureInfo.InvariantCulture); - var command = string.Format ("FETCH {0} {1} (CHANGEDSINCE {2})\r\n", GetFetchRange (min, max), query, modseqValue); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchSummaryContext (); - - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - async Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, bool doAsync, CancellationToken cancellationToken) - { - bool previewText; - - if (min < 0) - throw new ArgumentOutOfRangeException (nameof (min)); - - if (max != -1 && max < min) - throw new ArgumentOutOfRangeException (nameof (max)); - - var headerFields = ImapUtils.GetUniqueHeaders (headers); - - if (!supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - CheckState (true, false); - CheckAllowIndexes (); - - var query = FormatSummaryItems (ref items, headerFields, out previewText); - var modseqValue = modseq.ToString (CultureInfo.InvariantCulture); - var command = string.Format ("FETCH {0} {1} (CHANGEDSINCE {2})\r\n", GetFetchRange (min, max), query, modseqValue); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchSummaryContext (); - - ic.RegisterUntaggedHandler ("FETCH", FetchSummaryItemsAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (previewText) - await GetPreviewTextAsync (ctx, doAsync, cancellationToken).ConfigureAwait (false); - - return AsReadOnly (ctx.Messages); - } - - /// - /// Fetches the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// -or- - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (int min, int max, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, items, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// -or- - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (int min, int max, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, items, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return Fetch (min, max, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Asynchronously fetches the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Fetches the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, items, headers, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the messages between the two indexes, inclusive. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes, inclusive. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (int min, int max, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, items, headers, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// -or- - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, modseq, items, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// -or- - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, modseq, items, true, cancellationToken); - } - - /// - /// Fetches the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return Fetch (min, max, modseq, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Asynchronously fetches the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, modseq, items, ImapUtils.GetUniqueHeaders (headers), cancellationToken); - } - - /// - /// Fetches the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Fetch (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, modseq, items, headers, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously fetches the message summaries for the messages between the two indexes (inclusive) - /// that have a higher mod-sequence value than the one specified. - /// - /// - /// Fetches the message summaries for the messages between the two - /// indexes (inclusive) that have a higher mod-sequence value than the one - /// specified. - /// It should be noted that if another client has modified any message - /// in the folder, the IMAP server may choose to return information that was - /// not explicitly requested. It is therefore important to be prepared to - /// handle both additional fields on a for - /// messages that were requested as well as summaries for messages that were - /// not requested at all. - /// - /// An enumeration of summaries for the requested messages. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// The mod-sequence value. - /// The message summary items to fetch. - /// The desired header fields. - /// The cancellation token. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// One or more of the specified is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> FetchAsync (int min, int max, ulong modseq, MessageSummaryItems items, IEnumerable headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return FetchAsync (min, max, modseq, items, headers, true, cancellationToken); - } - - /// - /// Create a backing stream for use with the GetMessage, GetBodyPart, and GetStream methods. - /// - /// - /// Allows subclass implementations to override the type of stream - /// created for use with the GetMessage, GetBodyPart and GetStream methods. - /// This could be useful for subclass implementations that intend to implement - /// support for caching and/or for subclass implementations that want to use - /// temporary file streams instead of memory-based streams for larger amounts of - /// message data. - /// Subclasses that implement caching using this API should wait for - /// before adding the stream to their cache. - /// Streams returned by this method SHOULD clean up any allocated resources - /// such as deleting temporary files from the file system. - /// The will not be available for the various - /// GetMessage(), GetBodyPart() and GetStream() methods that take a message index rather - /// than a . It may also not be available if the IMAP server - /// response does not specify the UID value prior to sending the literal-string - /// token containing the message stream. - /// - /// - /// The stream. - /// The unique identifier of the message, if available. - /// The section of the message that is being fetched. - /// The starting offset of the message section being fetched. - /// The length of the stream being fetched, measured in bytes. - protected virtual Stream CreateStream (UniqueId? uid, string section, int offset, int length) - { - if (length > 4096) - return new MemoryBlockStream (); - - return new MemoryStream (length); - } - - /// - /// Commit a stream returned by . - /// - /// - /// Commits a stream returned by . - /// This method is called only after both the message data has successfully - /// been written to the stream returned by and a - /// has been obtained for the associated message. - /// For subclasses implementing caching, this method should be used for - /// committing the stream to their cache. - /// Subclass implementations may take advantage of the fact that - /// allows returning a new - /// reference if they move a file on the file system and wish to return a new - /// based on the new path, for example. - /// - /// - /// The stream. - /// The stream. - /// The unique identifier of the message. - /// The section of the message that the stream represents. - /// The starting offset of the message section. - /// The length of the stream, measured in bytes. - protected virtual Stream CommitStream (Stream stream, UniqueId uid, string section, int offset, int length) - { - return stream; - } - - async Task ParseHeadersAsync (Stream stream, bool doAsync, CancellationToken cancellationToken) - { - try { - return await Engine.ParseHeadersAsync (stream, doAsync, cancellationToken).ConfigureAwait (false); - } finally { - stream.Dispose (); - } - } - - async Task ParseMessageAsync (Stream stream, bool doAsync, CancellationToken cancellationToken) - { - bool dispose = !(stream is MemoryStream || stream is MemoryBlockStream); - - try { - return await Engine.ParseMessageAsync (stream, !dispose, doAsync, cancellationToken).ConfigureAwait (false); - } finally { - if (dispose) - stream.Dispose (); - } - } - - async Task ParseEntityAsync (Stream stream, bool dispose, bool doAsync, CancellationToken cancellationToken) - { - try { - return await Engine.ParseEntityAsync (stream, !dispose, doAsync, cancellationToken).ConfigureAwait (false); - } finally { - if (dispose) - stream.Dispose (); - } - } - - class Section - { - public int Index; - public UniqueId? UniqueId; - public Stream Stream; - public string Name; - public int Offset; - public int Length; - - public Section (Stream stream, int index, UniqueId? uid, string name, int offset, int length) - { - Stream = stream; - Offset = offset; - Length = length; - UniqueId = uid; - Index = index; - Name = name; - } - } - - abstract class FetchStreamContextBase : IDisposable - { - protected static readonly Task Complete = Task.FromResult (true); - public readonly List
Sections = new List
(); - readonly ITransferProgress progress; - - public FetchStreamContextBase (ITransferProgress progress) - { - this.progress = progress; - } - - public abstract Task AddAsync (Section section, bool doAsync, CancellationToken cancellationToken); - - public virtual bool Contains (int index, string specifier, out Section section) - { - section = null; - return false; - } - - public abstract Task SetUniqueIdAsync (int index, UniqueId uid, bool doAsync, CancellationToken cancellationToken); - - public void Report (long nread, long total) - { - if (progress == null) - return; - - progress.Report (nread, total); - } - - public void Dispose () - { - for (int i = 0; i < Sections.Count; i++) { - var section = Sections[i]; - - try { - section.Stream.Dispose (); - } catch (IOException) { - } - } - } - } - - class FetchStreamContext : FetchStreamContextBase - { - public FetchStreamContext (ITransferProgress progress) : base (progress) - { - } - - public override Task AddAsync (Section section, bool doAsync, CancellationToken cancellationToken) - { - Sections.Add (section); - return Complete; - } - - public bool TryGetSection (UniqueId uid, string specifier, out Section section, bool remove = false) - { - for (int i = 0; i < Sections.Count; i++) { - var item = Sections[i]; - - if (!item.UniqueId.HasValue || item.UniqueId.Value != uid) - continue; - - if (item.Name.Equals (specifier, StringComparison.OrdinalIgnoreCase)) { - if (remove) - Sections.RemoveAt (i); - - section = item; - return true; - } - } - - section = null; - - return false; - } - - public bool TryGetSection (int index, string specifier, out Section section, bool remove = false) - { - for (int i = 0; i < Sections.Count; i++) { - var item = Sections[i]; - - if (item.Index != index) - continue; - - if (item.Name.Equals (specifier, StringComparison.OrdinalIgnoreCase)) { - if (remove) - Sections.RemoveAt (i); - - section = item; - return true; - } - } - - section = null; - - return false; - } - - public override Task SetUniqueIdAsync (int index, UniqueId uid, bool doAsync, CancellationToken cancellationToken) - { - for (int i = 0; i < Sections.Count; i++) { - if (Sections[i].Index == index) - Sections[i].UniqueId = uid; - } - - return Complete; - } - } - - async Task FetchStreamAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - var annotations = new AnnotationsChangedEventArgs (index); - var labels = new MessageLabelsChangedEventArgs (index); - var flags = new MessageFlagsChangedEventArgs (index); - var modSeq = new ModSeqChangedEventArgs (index); - var ctx = (FetchStreamContextBase) ic.UserData; - var sectionBuilder = new StringBuilder (); - bool annotationsChanged = false; - bool modSeqChanged = false; - bool labelsChanged = false; - bool flagsChanged = false; - var buf = new byte[4096]; - long nread = 0, size = 0; - UniqueId? uid = null; - Section section; - Stream stream; - string name; - int n; - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - - do { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Eoln) { - // Note: Most likely the the message body was calculated to be 1 or 2 bytes too - // short (e.g. did not include the trailing ) and that is the EOLN we just - // reached. Ignore it and continue as normal. - // - // See https://github.com/jstedfast/MailKit/issues/954 for details. - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } - - if (token.Type == ImapTokenType.CloseParen) - break; - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - - var atom = (string) token.Value; - int offset = 0, length; - ulong modseq; - uint value; - - switch (atom.ToUpperInvariant ()) { - case "BODY": - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenBracket, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - sectionBuilder.Clear (); - - do { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseBracket) - break; - - if (token.Type == ImapTokenType.OpenParen) { - sectionBuilder.Append (" ("); - - do { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - // the header field names will generally be atoms or qstrings but may also be literals - switch (token.Type) { - case ImapTokenType.Literal: - sectionBuilder.Append (await engine.ReadLiteralAsync (doAsync, ic.CancellationToken).ConfigureAwait (false)); - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - sectionBuilder.Append ((string) token.Value); - break; - default: - throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - } - - sectionBuilder.Append (' '); - } while (true); - - if (sectionBuilder[sectionBuilder.Length - 1] == ' ') - sectionBuilder.Length--; - - sectionBuilder.Append (')'); - } else { - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - sectionBuilder.Append ((string) token.Value); - } - } while (true); - - ImapEngine.AssertToken (token, ImapTokenType.CloseBracket, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Atom) { - // this might be a region ("<###>") - var expr = (string) token.Value; - - if (expr.Length > 2 && expr[0] == '<' && expr[expr.Length - 1] == '>') { - var region = expr.Substring (1, expr.Length - 2); - - int.TryParse (region, NumberStyles.None, CultureInfo.InvariantCulture, out offset); - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } - } - - name = sectionBuilder.ToString (); - - switch (token.Type) { - case ImapTokenType.Literal: - length = (int) token.Value; - size += length; - - stream = CreateStream (uid, name, offset, length); - - try { - do { - if (doAsync) - n = await engine.Stream.ReadAsync (buf, 0, buf.Length, ic.CancellationToken).ConfigureAwait (false); - else - n = engine.Stream.Read (buf, 0, buf.Length, ic.CancellationToken); - - if (n > 0) { - stream.Write (buf, 0, n); - nread += n; - - ctx.Report (nread, size); - } else { - break; - } - } while (true); - - stream.Position = 0; - } catch { - stream.Dispose (); - throw; - } - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - var buffer = Encoding.UTF8.GetBytes ((string) token.Value); - length = buffer.Length; - nread += length; - size += length; - - stream = CreateStream (uid, name, offset, length); - - try { - stream.Write (buffer, 0, length); - ctx.Report (nread, size); - stream.Position = 0; - } catch { - stream.Dispose (); - throw; - } - break; - case ImapTokenType.Nil: - stream = CreateStream (uid, name, offset, 0); - length = 0; - break; - default: - throw ImapEngine.UnexpectedToken (ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - } - - if (uid.HasValue) - stream = CommitStream (stream, uid.Value, name, offset, length); - - // prevent leaks in the (invalid) case where a section may be returned twice - if (ctx.Contains (index, name, out section)) - section.Stream.Dispose (); - - section = new Section (stream, index, uid, name, offset, length); - await ctx.AddAsync (section, doAsync, ic.CancellationToken).ConfigureAwait (false); - break; - case "UID": - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - value = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - uid = new UniqueId (UidValidity, value); - - await ctx.SetUniqueIdAsync (index, uid.Value, doAsync, ic.CancellationToken).ConfigureAwait (false); - - annotations.UniqueId = uid.Value; - modSeq.UniqueId = uid.Value; - labels.UniqueId = uid.Value; - flags.UniqueId = uid.Value; - break; - case "MODSEQ": - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - modseq = ImapEngine.ParseNumber64 (token, false, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - if (modseq > HighestModSeq) - UpdateHighestModSeq (modseq); - - annotations.ModSeq = modseq; - modSeq.ModSeq = modseq; - labels.ModSeq = modseq; - flags.ModSeq = modseq; - modSeqChanged = true; - break; - case "FLAGS": - // 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, flags.Keywords, doAsync, ic.CancellationToken).ConfigureAwait (false); - flagsChanged = true; - break; - case "X-GM-LABELS": - // 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, doAsync, ic.CancellationToken).ConfigureAwait (false); - labelsChanged = true; - break; - case "ANNOTATION": - // 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, doAsync, ic.CancellationToken).ConfigureAwait (false); - annotationsChanged = true; - break; - default: - // 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 (doAsync, ic.CancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenParen) - await SkipParenthesizedList (engine, doAsync, ic.CancellationToken).ConfigureAwait (false); - break; - } - } while (true); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "FETCH", token); - - if (flagsChanged) - OnMessageFlagsChanged (flags); - - if (labelsChanged) - OnMessageLabelsChanged (labels); - - if (annotationsChanged) - OnAnnotationsChanged (annotations); - - if (modSeqChanged) - OnModSeqChanged (modSeq); - } - - static string GetBodyPartQuery (string partSpec, bool headersOnly, out string[] tags) - { - string query; - - if (headersOnly) { - tags = new string[1]; - - if (partSpec.Length > 0) { - query = string.Format ("BODY.PEEK[{0}.MIME]", partSpec); - tags[0] = partSpec + ".MIME"; - } else { - query = "BODY.PEEK[HEADER]"; - tags[0] = "HEADER"; - } - } else { - tags = new string[2]; - - if (partSpec.Length > 0) { - tags[0] = partSpec + ".MIME"; - tags[1] = partSpec; - } else { - tags[0] = "HEADER"; - tags[1] = "TEXT"; - } - - query = string.Format ("BODY.PEEK[{0}] BODY.PEEK[{1}]", tags[0], tags[1]); - } - - return query; - } - - async Task GetHeadersAsync (UniqueId uid, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - CheckState (true, false); - - var ic = new ImapCommand (Engine, cancellationToken, this, "UID FETCH %u (BODY.PEEK[HEADER])\r\n", uid.Id); - var ctx = new FetchStreamContext (progress); - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (uid, "HEADER", out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested message headers."); - } finally { - ctx.Dispose (); - } - - return await ParseHeadersAsync (section.Stream, doAsync, cancellationToken).ConfigureAwait (false); - } - - async Task GetHeadersAsync (UniqueId uid, string partSpecifier, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (partSpecifier == null) - throw new ArgumentNullException (nameof (partSpecifier)); - - CheckState (true, false); - - string[] tags; - - var command = string.Format ("UID FETCH {0} ({1})\r\n", uid, GetBodyPartQuery (partSpecifier, true, out tags)); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchStreamContext (progress); - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (uid, tags[0], out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested body part headers."); - } finally { - ctx.Dispose (); - } - - return await ParseHeadersAsync (section.Stream, doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Get the specified message headers. - /// - /// - /// Gets the specified message headers. - /// - /// The message headers. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override HeaderList GetHeaders (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetHeadersAsync (uid, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified message headers. - /// - /// - /// Gets the specified message headers. - /// - /// The message headers. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetHeadersAsync (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetHeadersAsync (uid, true, cancellationToken, progress); - } - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual HeaderList GetHeaders (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetHeadersAsync (uid, partSpecifier, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task GetHeadersAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetHeadersAsync (uid, partSpecifier, true, cancellationToken, progress); - } - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override HeaderList GetHeaders (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return GetHeaders (uid, part.PartSpecifier, cancellationToken, progress); - } - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetHeadersAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return GetHeadersAsync (uid, part.PartSpecifier, cancellationToken, progress); - } - - async Task GetHeadersAsync (int index, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - CheckState (true, false); - - var ic = new ImapCommand (Engine, cancellationToken, this, "FETCH %d (BODY.PEEK[HEADER])\r\n", index + 1); - var ctx = new FetchStreamContext (progress); - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (index, "HEADER", out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested message."); - } finally { - ctx.Dispose (); - } - - return await ParseHeadersAsync (section.Stream, doAsync, cancellationToken).ConfigureAwait (false); - } - - async Task GetHeadersAsync (int index, string partSpecifier, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (partSpecifier == null) - throw new ArgumentNullException (nameof (partSpecifier)); - - CheckState (true, false); - - string[] tags; - - var seqid = (index + 1).ToString (CultureInfo.InvariantCulture); - var command = string.Format ("FETCH {0} ({1})\r\n", seqid, GetBodyPartQuery (partSpecifier, true, out tags)); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchStreamContext (progress); - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (index, tags[0], out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested body part headers."); - } finally { - ctx.Dispose (); - } - - return await ParseHeadersAsync (section.Stream, doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Get the specified message headers. - /// - /// - /// Gets the specified message headers. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override HeaderList GetHeaders (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetHeadersAsync (index, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified message headers. - /// - /// - /// Gets the specified message headers. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetHeadersAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetHeadersAsync (index, true, cancellationToken, progress); - } - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual HeaderList GetHeaders (int index, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetHeadersAsync (index, partSpecifier, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task GetHeadersAsync (int index, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetHeadersAsync (index, partSpecifier, true, cancellationToken, progress); - } - - /// - /// Get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override HeaderList GetHeaders (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return GetHeaders (index, part.PartSpecifier, cancellationToken, progress); - } - - /// - /// Asynchronously get the specified body part headers. - /// - /// - /// Gets the specified body part headers. - /// - /// The body part headers. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested body part headers. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetHeadersAsync (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return GetHeadersAsync (index, part.PartSpecifier, cancellationToken, progress); - } - - async Task GetMessageAsync (UniqueId uid, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - CheckState (true, false); - - var ic = new ImapCommand (Engine, cancellationToken, this, "UID FETCH %u (BODY.PEEK[])\r\n", uid.Id); - var ctx = new FetchStreamContext (progress); - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (uid, string.Empty, out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested message."); - } finally { - ctx.Dispose (); - } - - return await ParseMessageAsync (section.Stream, doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Get the specified message. - /// - /// - /// Gets the specified message. - /// - /// The message. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override MimeMessage GetMessage (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetMessageAsync (uid, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified message. - /// - /// - /// Gets the specified message. - /// - /// The message. - /// The UID of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetMessageAsync (UniqueId uid, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetMessageAsync (uid, true, cancellationToken, progress); - } - - async Task GetMessageAsync (int index, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - CheckState (true, false); - - var ic = new ImapCommand (Engine, cancellationToken, this, "FETCH %d (BODY.PEEK[])\r\n", index + 1); - var ctx = new FetchStreamContext (progress); - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (index, string.Empty, out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested message."); - } finally { - ctx.Dispose (); - } - - return await ParseMessageAsync (section.Stream, doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Get the specified message. - /// - /// - /// Gets the specified message. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override MimeMessage GetMessage (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetMessageAsync (index, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified message. - /// - /// - /// Gets the specified message. - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetMessageAsync (index, true, cancellationToken, progress); - } - - async Task GetBodyPartAsync (UniqueId uid, string partSpecifier, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (partSpecifier == null) - throw new ArgumentNullException (nameof (partSpecifier)); - - CheckState (true, false); - - string[] tags; - - var command = string.Format ("UID FETCH {0} ({1})\r\n", uid, GetBodyPartQuery (partSpecifier, false, out tags)); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchStreamContext (progress); - ChainedStream chained = null; - bool dispose = false; - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - chained = new ChainedStream (); - - foreach (var tag in tags) { - if (!ctx.TryGetSection (uid, tag, out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested body part."); - - if (!(section.Stream is MemoryStream || section.Stream is MemoryBlockStream)) - dispose = true; - - chained.Add (section.Stream); - } - } catch { - if (chained != null) - chained.Dispose (); - - throw; - } finally { - ctx.Dispose (); - } - - var entity = await ParseEntityAsync (chained, dispose, doAsync, cancellationToken).ConfigureAwait (false); - - if (partSpecifier.Length == 0) { - for (int i = entity.Headers.Count; i > 0; i--) { - var header = entity.Headers[i - 1]; - - if (!header.Field.StartsWith ("Content-", StringComparison.OrdinalIgnoreCase)) - entity.Headers.RemoveAt (i - 1); - } - } - - return entity; - } - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The UID of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual MimeEntity GetBodyPart (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetBodyPartAsync (uid, partSpecifier, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The UID of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task GetBodyPartAsync (UniqueId uid, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetBodyPartAsync (uid, partSpecifier, true, cancellationToken, progress); - } - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// - /// - /// - /// The body part. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override MimeEntity GetBodyPart (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return GetBodyPart (uid, part.PartSpecifier, cancellationToken, progress); - } - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// - /// - /// - /// The body part. - /// The UID of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message body. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetBodyPartAsync (UniqueId uid, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return GetBodyPartAsync (uid, part.PartSpecifier, cancellationToken, progress); - } - - async Task GetBodyPartAsync (int index, string partSpecifier, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (partSpecifier == null) - throw new ArgumentNullException (nameof (partSpecifier)); - - CheckState (true, false); - - string[] tags; - - var seqid = (index + 1).ToString (CultureInfo.InvariantCulture); - var command = string.Format ("FETCH {0} ({1})\r\n", seqid, GetBodyPartQuery (partSpecifier, false, out tags)); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchStreamContext (progress); - ChainedStream chained = null; - bool dispose = false; - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - chained = new ChainedStream (); - - foreach (var tag in tags) { - if (!ctx.TryGetSection (index, tag, out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested body part."); - - if (!(section.Stream is MemoryStream || section.Stream is MemoryBlockStream)) - dispose = true; - - chained.Add (section.Stream); - } - } catch { - if (chained != null) - chained.Dispose (); - - throw; - } finally { - ctx.Dispose (); - } - - var entity = await ParseEntityAsync (chained, dispose, doAsync, cancellationToken).ConfigureAwait (false); - - if (partSpecifier.Length == 0) { - for (int i = entity.Headers.Count; i > 0; i--) { - var header = entity.Headers[i - 1]; - - if (!header.Field.StartsWith ("Content-", StringComparison.OrdinalIgnoreCase)) - entity.Headers.RemoveAt (i - 1); - } - } - - return entity; - } - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual MimeEntity GetBodyPart (int index, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetBodyPartAsync (index, partSpecifier, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part specifier. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task GetBodyPartAsync (int index, string partSpecifier, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetBodyPartAsync (index, partSpecifier, true, cancellationToken, progress); - } - - /// - /// Get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override MimeEntity GetBodyPart (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return GetBodyPart (index, part.PartSpecifier, cancellationToken, progress); - } - - /// - /// Asynchronously get the specified body part. - /// - /// - /// Gets the specified body part. - /// - /// The body part. - /// The index of the message. - /// The body part. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetBodyPartAsync (int index, BodyPart part, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return GetBodyPartAsync (index, part.PartSpecifier, cancellationToken, progress); - } - - async Task GetStreamAsync (UniqueId uid, int offset, int count, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (offset < 0) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException (nameof (count)); - - CheckState (true, false); - - if (count == 0) - return new MemoryStream (); - - var ic = new ImapCommand (Engine, cancellationToken, this, "UID FETCH %u (BODY.PEEK[]<%d.%d>)\r\n", uid.Id, offset, count); - var ctx = new FetchStreamContext (progress); - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (uid, string.Empty, out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); - } finally { - ctx.Dispose (); - } - - return section.Stream; - } - - async Task GetStreamAsync (int index, int offset, int count, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (offset < 0) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException (nameof (count)); - - CheckState (true, false); - - if (count == 0) - return new MemoryStream (); - - var ic = new ImapCommand (Engine, cancellationToken, this, "FETCH %d (BODY.PEEK[]<%d.%d>)\r\n", index + 1, offset, count); - var ctx = new FetchStreamContext (progress); - Section section; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (index, string.Empty, out section, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); - } finally { - ctx.Dispose (); - } - - return section.Stream; - } - - /// - /// Get a substream of the specified message. - /// - /// - /// Fetches a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Stream GetStream (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (uid, offset, count, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously gets a substream of the specified message. - /// - /// - /// Fetches a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The UID of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetStreamAsync (UniqueId uid, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (uid, offset, count, true, cancellationToken, progress); - } - - /// - /// Get a substream of the specified message. - /// - /// - /// Fetches a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Stream GetStream (int index, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (index, offset, count, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously gets a substream of the specified message. - /// - /// - /// Fetches a substream of the message. If the starting offset is beyond - /// the end of the message, an empty stream is returned. If the number of - /// bytes desired extends beyond the end of the message, a truncated stream - /// will be returned. - /// - /// The stream. - /// The index of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetStreamAsync (int index, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (index, offset, count, true, cancellationToken, progress); - } - - async Task GetStreamAsync (UniqueId uid, string section, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (section == null) - throw new ArgumentNullException (nameof (section)); - - CheckState (true, false); - - var command = string.Format ("UID FETCH {0} (BODY.PEEK[{1}])\r\n", uid, section); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchStreamContext (progress); - Section s; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (uid, section, out s, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); - } finally { - ctx.Dispose (); - } - - return s.Stream; - } - - /// - /// Get a substream of the specified body part. - /// - /// - /// Gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Stream GetStream (UniqueId uid, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (uid, section, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously gets a substream of the specified body part. - /// - /// - /// Gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetStreamAsync (UniqueId uid, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (uid, section, true, cancellationToken, progress); - } - - async Task GetStreamAsync (UniqueId uid, string section, int offset, int count, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (!uid.IsValid) - throw new ArgumentException ("The uid is invalid.", nameof (uid)); - - if (section == null) - throw new ArgumentNullException (nameof (section)); - - if (offset < 0) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException (nameof (count)); - - CheckState (true, false); - - if (count == 0) - return new MemoryStream (); - - 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); - var ctx = new FetchStreamContext (progress); - Section s; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (uid, section, out s, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); - } finally { - ctx.Dispose (); - } - - return s.Stream; - } - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. If the starting offset is beyond - /// the end of the specified section of the message, an empty stream is returned. If - /// the number of bytes desired extends beyond the end of the section, a truncated - /// stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Stream GetStream (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (uid, section, offset, count, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously gets a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. If the starting offset is beyond - /// the end of the specified section of the message, an empty stream is returned. If - /// the number of bytes desired extends beyond the end of the section, a truncated - /// stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The UID of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is invalid. - /// - /// - /// is null. - /// - /// - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetStreamAsync (UniqueId uid, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (uid, section, offset, count, true, cancellationToken, progress); - } - - async Task GetStreamAsync (int index, string section, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (section == null) - throw new ArgumentNullException (nameof (section)); - - CheckState (true, false); - - var seqid = (index + 1).ToString (CultureInfo.InvariantCulture); - var command = string.Format ("FETCH {0} (BODY.PEEK[{1}])\r\n", seqid, section); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchStreamContext (progress); - Section s; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (index, section, out s, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); - } finally { - ctx.Dispose (); - } - - return s.Stream; - } - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Stream GetStream (int index, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (index, section, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously gets a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetStreamAsync (int index, string section, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (index, section, true, cancellationToken, progress); - } - - async Task GetStreamAsync (int index, string section, int offset, int count, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (section == null) - throw new ArgumentNullException (nameof (section)); - - if (offset < 0) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0) - throw new ArgumentOutOfRangeException (nameof (count)); - - CheckState (true, false); - - if (count == 0) - return new MemoryStream (); - - 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); - var ctx = new FetchStreamContext (progress); - Section s; - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - - if (!ctx.TryGetSection (index, section, out s, true)) - throw new MessageNotFoundException ("The IMAP server did not return the requested stream."); - } finally { - ctx.Dispose (); - } - - return s.Stream; - } - - /// - /// Get a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. If the starting offset is beyond - /// the end of the specified section of the message, an empty stream is returned. If - /// the number of bytes desired extends beyond the end of the section, a truncated - /// stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Stream GetStream (int index, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (index, section, offset, count, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously gets a substream of the specified message. - /// - /// - /// Gets a substream of the specified message. If the starting offset is beyond - /// the end of the specified section of the message, an empty stream is returned. If - /// the number of bytes desired extends beyond the end of the section, a truncated - /// stream will be returned. - /// For more information about how to construct the , - /// see Section 6.4.5 of RFC3501. - /// - /// The stream. - /// The index of the message. - /// The desired section of the message. - /// The starting offset of the first desired byte. - /// The number of bytes desired. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// is out of range. - /// -or- - /// is negative. - /// -or- - /// is negative. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The IMAP server did not return the requested message stream. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task GetStreamAsync (int index, string section, int offset, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamAsync (index, section, offset, count, true, cancellationToken, progress); - } - - class FetchStreamCallbackContext : FetchStreamContextBase - { - readonly ImapFolder folder; - readonly object callback; - - public FetchStreamCallbackContext (ImapFolder folder, object callback, ITransferProgress progress) : base (progress) - { - this.folder = folder; - this.callback = callback; - } - - Task InvokeCallbackAsync (ImapFolder folder, int index, UniqueId uid, Stream stream, bool doAsync, CancellationToken cancellationToken) - { - if (doAsync) - return ((ImapFetchStreamAsyncCallback) callback) (folder, index, uid, stream, cancellationToken); - - ((ImapFetchStreamCallback) callback) (folder, index, uid, stream); - return Complete; - } - - public override async Task AddAsync (Section section, bool doAsync, CancellationToken cancellationToken) - { - if (section.UniqueId.HasValue) { - await InvokeCallbackAsync (folder, section.Index, section.UniqueId.Value, section.Stream, doAsync, cancellationToken).ConfigureAwait (false); - section.Stream.Dispose (); - } else { - Sections.Add (section); - } - } - - public override async Task SetUniqueIdAsync (int index, UniqueId uid, bool doAsync, CancellationToken cancellationToken) - { - for (int i = 0; i < Sections.Count; i++) { - if (Sections[i].Index == index) { - await InvokeCallbackAsync (folder, index, uid, Sections[i].Stream, doAsync, cancellationToken).ConfigureAwait (false); - Sections[i].Stream.Dispose (); - Sections.RemoveAt (i); - break; - } - } - } - } - - async Task GetStreamsAsync (IList uids, object callback, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (callback == null) - throw new ArgumentNullException (nameof (callback)); - - CheckState (true, false); - - if (uids.Count == 0) - return; - - var ctx = new FetchStreamCallbackContext (this, callback, progress); - var command = "UID FETCH %s (BODY.PEEK[])\r\n"; - - try { - foreach (var ic in Engine.CreateCommands (cancellationToken, this, command, uids)) { - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - } - } finally { - ctx.Dispose (); - } - } - - async Task GetStreamsAsync (IList indexes, object callback, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (callback == null) - throw new ArgumentNullException (nameof (callback)); - - CheckState (true, false); - CheckAllowIndexes (); - - if (indexes.Count == 0) - return; - - var set = ImapUtils.FormatIndexSet (indexes); - var command = string.Format ("FETCH {0} (UID BODY.PEEK[])\r\n", set); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchStreamCallbackContext (this, callback, progress); - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - } finally { - ctx.Dispose (); - } - } - - async Task GetStreamsAsync (int min, int max, object callback, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - if (min < 0) - throw new ArgumentOutOfRangeException (nameof (min)); - - if (max != -1 && max < min) - throw new ArgumentOutOfRangeException (nameof (max)); - - if (callback == null) - throw new ArgumentNullException (nameof (callback)); - - CheckState (true, false); - CheckAllowIndexes (); - - if (min == Count) - return; - - var command = string.Format ("FETCH {0} (UID BODY.PEEK[])\r\n", GetFetchRange (min, max)); - var ic = new ImapCommand (Engine, cancellationToken, this, command); - var ctx = new FetchStreamCallbackContext (this, callback, progress); - - ic.RegisterUntaggedHandler ("FETCH", FetchStreamAsync); - ic.UserData = ctx; - - Engine.QueueCommand (ic); - - try { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("FETCH", ic); - } finally { - ctx.Dispose (); - } - } - - /// - /// Get the streams for the specified messages. - /// - /// - /// Gets the streams for the specified messages. - /// - /// The uids of the messages. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual void GetStreams (IList uids, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - GetStreamsAsync (uids, callback, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the streams for the specified messages. - /// - /// - /// Asynchronously gets the streams for the specified messages. - /// - /// An awaitable task. - /// The uids of the messages. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task GetStreamsAsync (IList uids, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamsAsync (uids, callback, true, cancellationToken, progress); - } - - /// - /// Get the streams for the specified messages. - /// - /// - /// Gets the streams for the specified messages. - /// - /// The indexes of the messages. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual void GetStreams (IList indexes, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - GetStreamsAsync (indexes, callback, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the streams for the specified messages. - /// - /// - /// Asynchronously gets the streams for the specified messages. - /// - /// An awaitable task. - /// The indexes of the messages. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task GetStreamsAsync (IList indexes, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamsAsync (indexes, callback, true, cancellationToken, progress); - } - - /// - /// Get the streams for the specified messages. - /// - /// - /// Gets the streams for the specified messages. - /// - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual void GetStreams (int min, int max, ImapFetchStreamCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - GetStreamsAsync (min, max, callback, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the streams for the specified messages. - /// - /// - /// Asynchronously gets the streams for the specified messages. - /// - /// An awaitable task. - /// The minimum index. - /// The maximum index, or -1 to specify no upper bound. - /// - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is out of range. - /// -or- - /// is out of range. - /// - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task GetStreamsAsync (int min, int max, ImapFetchStreamAsyncCallback callback, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - return GetStreamsAsync (min, max, callback, true, cancellationToken, progress); - } - } -} diff --git a/src/MailKit/Net/Imap/ImapFolderFlags.cs b/src/MailKit/Net/Imap/ImapFolderFlags.cs deleted file mode 100644 index 1884e53..0000000 --- a/src/MailKit/Net/Imap/ImapFolderFlags.cs +++ /dev/null @@ -1,2888 +0,0 @@ -// -// ImapFolderFlags.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MailKit.Net.Imap -{ - public partial class ImapFolder - { - void ProcessUnmodified (ImapCommand ic, ref UniqueIdSet uids, ulong? modseq) - { - if (modseq.HasValue) { - foreach (var rc in ic.RespCodes.OfType ()) { - if (uids != null) - uids.AddRange (rc.UidSet); - else - uids = rc.UidSet; - } - } - } - - IList GetUnmodified (ImapCommand ic, ulong? modseq) - { - if (modseq.HasValue) { - var rc = ic.RespCodes.OfType ().FirstOrDefault (); - - if (rc != null) { - var unmodified = new int[rc.UidSet.Count]; - for (int i = 0; i < unmodified.Length; i++) - unmodified[i] = (int) (rc.UidSet[i].Id - 1); - - return unmodified; - } - } - - return new int[0]; - } - - async Task> ModifyFlagsAsync (IList uids, ulong? modseq, MessageFlags flags, HashSet keywords, string action, bool doAsync, CancellationToken cancellationToken) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (modseq.HasValue && !supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - CheckState (true, true); - - if (uids.Count == 0) - return new UniqueId[0]; - - var flaglist = ImapUtils.FormatFlagsList (flags & PermanentFlags, keywords != null ? keywords.Count : 0); - var keywordList = keywords != null ? keywords.ToArray () : new object[0]; - UniqueIdSet unmodified = null; - var @params = string.Empty; - - if (modseq.HasValue) - @params = string.Format (CultureInfo.InvariantCulture, " (UNCHANGEDSINCE {0})", modseq.Value); - - var command = string.Format ("UID STORE %s{0} {1} {2}\r\n", @params, action, flaglist); - - foreach (var ic in Engine.QueueCommands (cancellationToken, this, command, uids, keywordList)) { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("STORE", ic); - - ProcessUnmodified (ic, ref unmodified, modseq); - } - - if (unmodified == null) - return new UniqueId[0]; - - return unmodified; - } - - /// - /// Adds a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void AddFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - bool emptyUserFlags = keywords == null || keywords.Count == 0; - - if ((flags & SettableFlags) == 0 && emptyUserFlags) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - ModifyFlagsAsync (uids, null, flags, keywords, silent ? "+FLAGS.SILENT" : "+FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task AddFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - bool emptyUserFlags = keywords == null || keywords.Count == 0; - - if ((flags & SettableFlags) == 0 && emptyUserFlags) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (uids, null, flags, keywords, silent ? "+FLAGS.SILENT" : "+FLAGS", true, cancellationToken); - } - - /// - /// Removes a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void RemoveFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - ModifyFlagsAsync (uids, null, flags, keywords, silent ? "-FLAGS.SILENT" : "-FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task RemoveFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (uids, null, flags, keywords, silent ? "-FLAGS.SILENT" : "-FLAGS", true, cancellationToken); - } - - /// - /// Sets the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The UIDs of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void SetFlags (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - ModifyFlagsAsync (uids, null, flags, keywords, silent ? "FLAGS.SILENT" : "FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SetFlagsAsync (IList uids, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return ModifyFlagsAsync (uids, null, flags, keywords, silent ? "FLAGS.SILENT" : "FLAGS", true, cancellationToken); - } - - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList AddFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (uids, modseq, flags, keywords, silent ? "+FLAGS.SILENT" : "+FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> AddFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (uids, modseq, flags, keywords, silent ? "+FLAGS.SILENT" : "+FLAGS", true, cancellationToken); - } - - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList RemoveFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (uids, modseq, flags, keywords, silent ? "-FLAGS.SILENT" : "-FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> RemoveFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (uids, modseq, flags, keywords, silent ? "-FLAGS.SILENT" : "-FLAGS", true, cancellationToken); - } - - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList SetFlags (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return ModifyFlagsAsync (uids, modseq, flags, keywords, silent ? "FLAGS.SILENT" : "FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> SetFlagsAsync (IList uids, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return ModifyFlagsAsync (uids, modseq, flags, keywords, silent ? "FLAGS.SILENT" : "FLAGS", true, cancellationToken); - } - - async Task> ModifyFlagsAsync (IList indexes, ulong? modseq, MessageFlags flags, HashSet keywords, string action, bool doAsync, CancellationToken cancellationToken) - { - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (modseq.HasValue && !supportsModSeq) - throw new NotSupportedException ("The ImapFolder does not support mod-sequences."); - - CheckState (true, true); - - if (indexes.Count == 0) - return new int[0]; - - var flaglist = ImapUtils.FormatFlagsList (flags & PermanentFlags, keywords != null ? keywords.Count : 0); - var keywordList = keywords != null ? keywords.ToArray () : new object [0]; - var set = ImapUtils.FormatIndexSet (indexes); - var @params = string.Empty; - - if (modseq.HasValue) - @params = string.Format (CultureInfo.InvariantCulture, " (UNCHANGEDSINCE {0})", modseq.Value); - - var format = string.Format ("STORE {0}{1} {2} {3}\r\n", set, @params, action, flaglist); - var ic = Engine.QueueCommand (cancellationToken, this, format, keywordList); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("STORE", ic); - - return GetUnmodified (ic, modseq); - } - - /// - /// Adds a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// The indexes of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void AddFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - ModifyFlagsAsync (indexes, null, flags, keywords, silent ? "+FLAGS.SILENT" : "+FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously adds a set of flags to the specified messages. - /// - /// - /// Adds a set of flags to the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task AddFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (indexes, null, flags, keywords, silent ? "+FLAGS.SILENT" : "+FLAGS", true, cancellationToken); - } - - /// - /// Removes a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// The indexes of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void RemoveFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - ModifyFlagsAsync (indexes, null, flags, keywords, silent ? "-FLAGS.SILENT" : "-FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously removes a set of flags from the specified messages. - /// - /// - /// Removes a set of flags from the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task RemoveFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (indexes, null, flags, keywords, silent ? "-FLAGS.SILENT" : "-FLAGS", true, cancellationToken); - } - - /// - /// Sets the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// The indexes of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void SetFlags (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - ModifyFlagsAsync (indexes, null, flags, keywords, silent ? "FLAGS.SILENT" : "FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously sets the flags of the specified messages. - /// - /// - /// Sets the flags of the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SetFlagsAsync (IList indexes, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return ModifyFlagsAsync (indexes, null, flags, keywords, silent ? "FLAGS.SILENT" : "FLAGS", true, cancellationToken); - } - - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList AddFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (indexes, modseq, flags, keywords, silent ? "+FLAGS.SILENT" : "+FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of flags to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to add. - /// A set of user-defined flags to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> AddFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (indexes, modseq, flags, keywords, silent ? "+FLAGS.SILENT" : "+FLAGS", true, cancellationToken); - } - - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList RemoveFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (indexes, modseq, flags, keywords, silent ? "-FLAGS.SILENT" : "-FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of flags from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to remove. - /// A set of user-defined flags to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No flags were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> RemoveFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if ((flags & SettableFlags) == 0 && (keywords == null || keywords.Count == 0)) - throw new ArgumentException ("No flags were specified.", nameof (flags)); - - return ModifyFlagsAsync (indexes, modseq, flags, keywords, silent ? "-FLAGS.SILENT" : "-FLAGS", true, cancellationToken); - } - - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList SetFlags (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return ModifyFlagsAsync (indexes, modseq, flags, keywords, silent ? "FLAGS.SILENT" : "FLAGS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the flags of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The message flags to set. - /// A set of user-defined flags to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The does not support mod-sequences. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> SetFlagsAsync (IList indexes, ulong modseq, MessageFlags flags, HashSet keywords, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - return ModifyFlagsAsync (indexes, modseq, flags, keywords, silent ? "FLAGS.SILENT" : "FLAGS", true, cancellationToken); - } - - string LabelListToString (IList labels, ICollection args) - { - var list = new StringBuilder ("("); - - for (int i = 0; i < labels.Count; i++) { - if (i > 0) - list.Append (' '); - - if (labels[i] == null) { - list.Append ("NIL"); - continue; - } - - switch (labels[i]) { - case "\\AllMail": - case "\\Drafts": - case "\\Important": - case "\\Inbox": - case "\\Spam": - case "\\Sent": - case "\\Starred": - case "\\Trash": - list.Append (labels[i]); - break; - default: - list.Append ("%S"); - args.Add (Engine.EncodeMailboxName (labels[i])); - break; - } - } - - list.Append (')'); - - return list.ToString (); - } - - async Task> ModifyLabelsAsync (IList uids, ulong? modseq, IList labels, string action, bool doAsync, CancellationToken cancellationToken) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if ((Engine.Capabilities & ImapCapabilities.GMailExt1) == 0) - throw new NotSupportedException ("The IMAP server does not support the Google Mail extensions."); - - CheckState (true, true); - - if (uids.Count == 0) - return new UniqueId[0]; - - var @params = string.Empty; - - if (modseq.HasValue) - @params = string.Format (CultureInfo.InvariantCulture, " (UNCHANGEDSINCE {0})", modseq.Value); - - var args = new List (); - var list = LabelListToString (labels, args); - var command = string.Format ("UID STORE %s{0} {1} {2}\r\n", @params, action, list); - UniqueIdSet unmodified = null; - - foreach (var ic in Engine.QueueCommands (cancellationToken, this, command, uids, args.ToArray ())) { - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("STORE", ic); - - ProcessUnmodified (ic, ref unmodified, modseq); - } - - if (unmodified == null) - return new UniqueId[0]; - - return unmodified; - } - - /// - /// Add a set of labels to the specified messages. - /// - /// - /// Adds a set of labels to the specified messages. - /// - /// The UIDs of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void AddLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - ModifyLabelsAsync (uids, null, labels, silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously add a set of labels to the specified messages. - /// - /// - /// Adds a set of labels to the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task AddLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (uids, null, labels, silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS", true, cancellationToken); - } - - /// - /// Remove a set of labels from the specified messages. - /// - /// - /// Removes a set of labels from the specified messages. - /// - /// The UIDs of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void RemoveLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - ModifyLabelsAsync (uids, null, labels, silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously remove a set of labels from the specified messages. - /// - /// - /// Removes a set of labels from the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task RemoveLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (uids, null, labels, silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS", true, cancellationToken); - } - - /// - /// Set the labels of the specified messages. - /// - /// - /// Sets the labels of the specified messages. - /// - /// The UIDs of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void SetLabels (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - ModifyLabelsAsync (uids, null, labels, silent ? "X-GM-LABELS.SILENT" : "X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously set the labels of the specified messages. - /// - /// - /// Sets the labels of the specified messages. - /// - /// An asynchronous task context. - /// The UIDs of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SetLabelsAsync (IList uids, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - return ModifyLabelsAsync (uids, null, labels, silent ? "X-GM-LABELS.SILENT" : "X-GM-LABELS", true, cancellationToken); - } - - /// - /// Add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList AddLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (uids, modseq, labels, silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> AddLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (uids, modseq, labels, silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS", true, cancellationToken); - } - - /// - /// Remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList RemoveLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (uids, modseq, labels, silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> RemoveLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (uids, modseq, labels, silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS", true, cancellationToken); - } - - /// - /// Set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList SetLabels (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - return ModifyLabelsAsync (uids, modseq, labels, silent ? "X-GM-LABELS.SILENT" : "X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The unique IDs of the messages that were not updated. - /// The UIDs of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> SetLabelsAsync (IList uids, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - return ModifyLabelsAsync (uids, modseq, labels, silent ? "X-GM-LABELS.SILENT" : "X-GM-LABELS", true, cancellationToken); - } - - async Task> ModifyLabelsAsync (IList indexes, ulong? modseq, IList labels, string action, bool doAsync, CancellationToken cancellationToken) - { - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if ((Engine.Capabilities & ImapCapabilities.GMailExt1) == 0) - throw new NotSupportedException ("The IMAP server does not support the Google Mail extensions."); - - CheckState (true, true); - - if (indexes.Count == 0) - return new int[0]; - - var set = ImapUtils.FormatIndexSet (indexes); - var @params = string.Empty; - - if (modseq.HasValue) - @params = string.Format (CultureInfo.InvariantCulture, " (UNCHANGEDSINCE {0})", modseq.Value); - - var args = new List (); - var list = LabelListToString (labels, args); - var format = string.Format ("STORE {0}{1} {2} {3}\r\n", set, @params, action, list); - var ic = Engine.QueueCommand (cancellationToken, this, format, args.ToArray ()); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("STORE", ic); - - return GetUnmodified (ic, modseq); - } - - /// - /// Add a set of labels to the specified messages. - /// - /// - /// Adds a set of labels to the specified messages. - /// - /// The indexes of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void AddLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - ModifyLabelsAsync (indexes, null, labels, silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously add a set of labels to the specified messages. - /// - /// - /// Adds a set of labels to the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task AddLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (indexes, null, labels, silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS", true, cancellationToken); - } - - /// - /// Remove a set of labels from the specified messages. - /// - /// - /// Removes a set of labels from the specified messages. - /// - /// The indexes of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void RemoveLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - ModifyLabelsAsync (indexes, null, labels, silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously remove a set of labels from the specified messages. - /// - /// - /// Removes a set of labels from the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task RemoveLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (indexes, null, labels, silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS", true, cancellationToken); - } - - /// - /// Sets the labels of the specified messages. - /// - /// - /// Sets the labels of the specified messages. - /// - /// The indexes of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override void SetLabels (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - ModifyLabelsAsync (indexes, null, labels, silent ? "X-GM-LABELS.SILENT" : "X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously sets the labels of the specified messages. - /// - /// - /// Sets the labels of the specified messages. - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SetLabelsAsync (IList indexes, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - return ModifyLabelsAsync (indexes, null, labels, silent ? "X-GM-LABELS.SILENT" : "X-GM-LABELS", true, cancellationToken); - } - - /// - /// Add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList AddLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (indexes, modseq, labels, silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously add a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Adds a set of labels to the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to add. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> AddLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (indexes, modseq, labels, silent ? "+X-GM-LABELS.SILENT" : "+X-GM-LABELS", true, cancellationToken); - } - - /// - /// Remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList RemoveLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (indexes, modseq, labels, silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously remove a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Removes a set of labels from the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to remove. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// -or- - /// No labels were specified. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> RemoveLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - if (labels.Count == 0) - throw new ArgumentException ("No labels were specified.", nameof (labels)); - - return ModifyLabelsAsync (indexes, modseq, labels, silent ? "-X-GM-LABELS.SILENT" : "-X-GM-LABELS", true, cancellationToken); - } - - /// - /// Set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList SetLabels (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - return ModifyLabelsAsync (indexes, modseq, labels, silent ? "X-GM-LABELS.SILENT" : "X-GM-LABELS", false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously set the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// - /// Sets the labels of the specified messages only if their mod-sequence value is less than the specified value. - /// - /// The indexes of the messages that were not updated. - /// The indexes of the messages. - /// The mod-sequence value. - /// The labels to set. - /// If set to true, no events will be emitted. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the is invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open in read-write mode. - /// - /// - /// The IMAP server does not support the Google Mail Extensions. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> SetLabelsAsync (IList indexes, ulong modseq, IList labels, bool silent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (labels == null) - throw new ArgumentNullException (nameof (labels)); - - return ModifyLabelsAsync (indexes, modseq, labels, silent ? "X-GM-LABELS.SILENT" : "X-GM-LABELS", true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Imap/ImapFolderSearch.cs b/src/MailKit/Net/Imap/ImapFolderSearch.cs deleted file mode 100644 index fe26cce..0000000 --- a/src/MailKit/Net/Imap/ImapFolderSearch.cs +++ /dev/null @@ -1,1773 +0,0 @@ -// -// ImapFolderSearch.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MailKit.Search; - -namespace MailKit.Net.Imap -{ - public partial class ImapFolder - { - static bool IsAscii (string text) - { - for (int i = 0; i < text.Length; i++) { - if (text[i] > 127) - return false; - } - - return true; - } - - static string FormatDateTime (DateTime date) - { - return date.ToString ("d-MMM-yyyy", CultureInfo.InvariantCulture); - } - - bool IsBadCharset (ImapCommand ic, string charset) - { - // Note: if `charset` is null, then the charset is actually US-ASCII... - return ic.Response == ImapCommandResponse.No && - ic.RespCodes.Any (rc => rc.Type == ImapResponseCodeType.BadCharset) && - charset != null && !Engine.SupportedCharsets.Contains (charset); - } - - void AddTextArgument (StringBuilder builder, List args, string text, ref string charset) - { - if (IsAscii (text)) { - builder.Append ("%S"); - args.Add (text); - return; - } - - if (Engine.SupportedCharsets.Contains ("UTF-8")) { - builder.Append ("%S"); - charset = "UTF-8"; - args.Add (text); - return; - } - - // force the text into US-ASCII... - var buffer = new byte[text.Length]; - for (int i = 0; i < text.Length; i++) - buffer[i] = (byte) text[i]; - - builder.Append ("%L"); - args.Add (buffer); - } - - void BuildQuery (StringBuilder builder, SearchQuery query, List args, bool parens, ref string charset) - { - AnnotationSearchQuery annotation; - NumericSearchQuery numeric; - FilterSearchQuery filter; - HeaderSearchQuery header; - BinarySearchQuery binary; - UnarySearchQuery unary; - DateSearchQuery date; - TextSearchQuery text; - UidSearchQuery uid; - - switch (query.Term) { - case SearchTerm.All: - builder.Append ("ALL"); - break; - case SearchTerm.And: - binary = (BinarySearchQuery) query; - if (parens) - builder.Append ('('); - BuildQuery (builder, binary.Left, args, false, ref charset); - builder.Append (' '); - BuildQuery (builder, binary.Right, args, false, ref charset); - if (parens) - builder.Append (')'); - break; - case SearchTerm.Annotation: - if ((Engine.Capabilities & ImapCapabilities.Annotate) == 0) - throw new NotSupportedException ("The ANNOTATION search term is not supported by the IMAP server."); - - annotation = (AnnotationSearchQuery) query; - builder.AppendFormat ("ANNOTATION {0} {1} %S", annotation.Entry, annotation.Attribute); - args.Add (annotation.Value); - break; - case SearchTerm.Answered: - builder.Append ("ANSWERED"); - break; - case SearchTerm.BccContains: - text = (TextSearchQuery) query; - builder.Append ("BCC "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.BodyContains: - text = (TextSearchQuery) query; - builder.Append ("BODY "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.CcContains: - text = (TextSearchQuery) query; - builder.Append ("CC "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.Deleted: - builder.Append ("DELETED"); - break; - case SearchTerm.DeliveredAfter: - date = (DateSearchQuery) query; - builder.AppendFormat ("SINCE {0}", FormatDateTime (date.Date)); - break; - case SearchTerm.DeliveredBefore: - date = (DateSearchQuery) query; - builder.AppendFormat ("BEFORE {0}", FormatDateTime (date.Date)); - break; - case SearchTerm.DeliveredOn: - date = (DateSearchQuery) query; - builder.AppendFormat ("ON {0}", FormatDateTime (date.Date)); - break; - case SearchTerm.Draft: - builder.Append ("DRAFT"); - break; - case SearchTerm.Filter: - if ((Engine.Capabilities & ImapCapabilities.Filters) == 0) - throw new NotSupportedException ("The FILTER search term is not supported by the IMAP server."); - - filter = (FilterSearchQuery) query; - builder.Append ("FILTER %S"); - args.Add (filter.Name); - break; - case SearchTerm.Flagged: - builder.Append ("FLAGGED"); - break; - case SearchTerm.FromContains: - text = (TextSearchQuery) query; - builder.Append ("FROM "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.Fuzzy: - if ((Engine.Capabilities & ImapCapabilities.FuzzySearch) == 0) - throw new NotSupportedException ("The FUZZY search term is not supported by the IMAP server."); - - builder.Append ("FUZZY "); - unary = (UnarySearchQuery) query; - BuildQuery (builder, unary.Operand, args, true, ref charset); - break; - case SearchTerm.HeaderContains: - header = (HeaderSearchQuery) query; - builder.AppendFormat ("HEADER {0} ", header.Field); - AddTextArgument (builder, args, header.Value, ref charset); - break; - case SearchTerm.Keyword: - text = (TextSearchQuery) query; - builder.Append ("KEYWORD "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.LargerThan: - numeric = (NumericSearchQuery) query; - builder.AppendFormat (CultureInfo.InvariantCulture, "LARGER {0}", numeric.Value); - break; - case SearchTerm.MessageContains: - text = (TextSearchQuery) query; - builder.Append ("TEXT "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.ModSeq: - numeric = (NumericSearchQuery) query; - builder.AppendFormat (CultureInfo.InvariantCulture, "MODSEQ {0}", numeric.Value); - break; - case SearchTerm.New: - builder.Append ("NEW"); - break; - case SearchTerm.Not: - builder.Append ("NOT "); - unary = (UnarySearchQuery) query; - BuildQuery (builder, unary.Operand, args, true, ref charset); - break; - case SearchTerm.NotAnswered: - builder.Append ("UNANSWERED"); - break; - case SearchTerm.NotDeleted: - builder.Append ("UNDELETED"); - break; - case SearchTerm.NotDraft: - builder.Append ("UNDRAFT"); - break; - case SearchTerm.NotFlagged: - builder.Append ("UNFLAGGED"); - break; - case SearchTerm.NotKeyword: - text = (TextSearchQuery) query; - builder.Append ("UNKEYWORD "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.NotRecent: - builder.Append ("OLD"); - break; - case SearchTerm.NotSeen: - builder.Append ("UNSEEN"); - break; - case SearchTerm.Older: - if ((Engine.Capabilities & ImapCapabilities.Within) == 0) - throw new NotSupportedException ("The OLDER search term is not supported by the IMAP server."); - - numeric = (NumericSearchQuery) query; - builder.AppendFormat (CultureInfo.InvariantCulture, "OLDER {0}", numeric.Value); - break; - case SearchTerm.Or: - builder.Append ("OR "); - binary = (BinarySearchQuery) query; - BuildQuery (builder, binary.Left, args, true, ref charset); - builder.Append (' '); - BuildQuery (builder, binary.Right, args, true, ref charset); - break; - case SearchTerm.Recent: - builder.Append ("RECENT"); - break; - case SearchTerm.Seen: - builder.Append ("SEEN"); - break; - case SearchTerm.SentBefore: - date = (DateSearchQuery) query; - builder.AppendFormat ("SENTBEFORE {0}", FormatDateTime (date.Date)); - break; - case SearchTerm.SentOn: - date = (DateSearchQuery) query; - builder.AppendFormat ("SENTON {0}", FormatDateTime (date.Date)); - break; - case SearchTerm.SentSince: - date = (DateSearchQuery) query; - builder.AppendFormat ("SENTSINCE {0}", FormatDateTime (date.Date)); - break; - case SearchTerm.SmallerThan: - numeric = (NumericSearchQuery) query; - builder.AppendFormat (CultureInfo.InvariantCulture, "SMALLER {0}", numeric.Value); - break; - case SearchTerm.SubjectContains: - text = (TextSearchQuery) query; - builder.Append ("SUBJECT "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.ToContains: - text = (TextSearchQuery) query; - builder.Append ("TO "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.Uid: - uid = (UidSearchQuery) query; - builder.AppendFormat ("UID {0}", UniqueIdSet.ToString (uid.Uids)); - break; - case SearchTerm.Younger: - if ((Engine.Capabilities & ImapCapabilities.Within) == 0) - throw new NotSupportedException ("The YOUNGER search term is not supported by the IMAP server."); - - numeric = (NumericSearchQuery) query; - builder.AppendFormat (CultureInfo.InvariantCulture, "YOUNGER {0}", numeric.Value); - break; - case SearchTerm.GMailMessageId: - if ((Engine.Capabilities & ImapCapabilities.GMailExt1) == 0) - throw new NotSupportedException ("The X-GM-MSGID search term is not supported by the IMAP server."); - - numeric = (NumericSearchQuery) query; - builder.AppendFormat (CultureInfo.InvariantCulture, "X-GM-MSGID {0}", numeric.Value); - break; - case SearchTerm.GMailThreadId: - if ((Engine.Capabilities & ImapCapabilities.GMailExt1) == 0) - throw new NotSupportedException ("The X-GM-THRID search term is not supported by the IMAP server."); - - numeric = (NumericSearchQuery) query; - builder.AppendFormat (CultureInfo.InvariantCulture, "X-GM-THRID {0}", numeric.Value); - break; - case SearchTerm.GMailLabels: - if ((Engine.Capabilities & ImapCapabilities.GMailExt1) == 0) - throw new NotSupportedException ("The X-GM-LABELS search term is not supported by the IMAP server."); - - text = (TextSearchQuery) query; - builder.Append ("X-GM-LABELS "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - case SearchTerm.GMailRaw: - if ((Engine.Capabilities & ImapCapabilities.GMailExt1) == 0) - throw new NotSupportedException ("The X-GM-RAW search term is not supported by the IMAP server."); - - text = (TextSearchQuery) query; - builder.Append ("X-GM-RAW "); - AddTextArgument (builder, args, text.Text, ref charset); - break; - default: - throw new ArgumentOutOfRangeException (); - } - } - - string BuildQueryExpression (SearchQuery query, List args, out string charset) - { - var builder = new StringBuilder (); - - charset = null; - - BuildQuery (builder, query, args, false, ref charset); - - return builder.ToString (); - } - - string BuildSortOrder (IList orderBy) - { - var builder = new StringBuilder (); - - builder.Append ('('); - for (int i = 0; i < orderBy.Count; i++) { - if (builder.Length > 1) - builder.Append (' '); - - if (orderBy[i].Order == SortOrder.Descending) - builder.Append ("REVERSE "); - - switch (orderBy[i].Type) { - case OrderByType.Annotation: - if ((Engine.Capabilities & ImapCapabilities.Annotate) == 0) - throw new NotSupportedException ("The ANNOTATION search term is not supported by the IMAP server."); - - var annotation = (OrderByAnnotation) orderBy[i]; - builder.AppendFormat ("ANNOTATION {0} {1}", annotation.Entry, annotation.Attribute); - break; - case OrderByType.Arrival: builder.Append ("ARRIVAL"); break; - case OrderByType.Cc: builder.Append ("CC"); break; - case OrderByType.Date: builder.Append ("DATE"); break; - case OrderByType.DisplayFrom: - if ((Engine.Capabilities & ImapCapabilities.SortDisplay) == 0) - throw new NotSupportedException ("The IMAP server does not support the SORT=DISPLAY extension."); - - builder.Append ("DISPLAYFROM"); - break; - case OrderByType.DisplayTo: - if ((Engine.Capabilities & ImapCapabilities.SortDisplay) == 0) - throw new NotSupportedException ("The IMAP server does not support the SORT=DISPLAY extension."); - - builder.Append ("DISPLAYTO"); - break; - case OrderByType.From: builder.Append ("FROM"); break; - case OrderByType.Size: builder.Append ("SIZE"); break; - case OrderByType.Subject: builder.Append ("SUBJECT"); break; - case OrderByType.To: builder.Append ("TO"); break; - default: throw new ArgumentOutOfRangeException (); - } - } - builder.Append (')'); - - return builder.ToString (); - } - - static async Task SearchMatchesAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var results = (SearchResults) ic.UserData; - var uids = results.UniqueIds; - ImapToken token; - uint uid; - - do { - token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - // keep reading UIDs until we get to the end of the line or until we get a "(MODSEQ ####)" - if (token.Type == ImapTokenType.Eoln || token.Type == ImapTokenType.OpenParen) - break; - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - uid = ImapEngine.ParseNumber (token, true, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "SEARCH", token); - uids.Add (new UniqueId (ic.Folder.UidValidity, uid)); - } while (true); - - if (token.Type == ImapTokenType.OpenParen) { - await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - do { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "SEARCH", token); - - var atom = (string) token.Value; - - switch (atom.ToUpperInvariant ()) { - case "MODSEQ": - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - results.ModSeq = ImapEngine.ParseNumber64 (token, false, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - break; - } - - token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } while (token.Type != ImapTokenType.Eoln); - } - - results.UniqueIds = uids; - } - - static async Task ESearchMatchesAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - var results = (SearchResults) ic.UserData; - int parenDepth = 0; - //bool uid = false; - string atom; - string tag; - - if (token.Type == ImapTokenType.OpenParen) { - // optional search correlator - do { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - atom = (string) token.Value; - - if (atom == "TAG") { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - tag = (string) token.Value; - - if (tag != ic.Tag) - throw new ImapProtocolException ("Unexpected TAG value in untagged ESEARCH response: " + tag); - } - } while (true); - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } - - if (token.Type == ImapTokenType.Atom && ((string) token.Value) == "UID") { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - //uid = true; - } - - do { - if (token.Type == ImapTokenType.CloseParen) { - if (parenDepth == 0) - throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - parenDepth--; - } - - if (token.Type == ImapTokenType.Eoln) { - // unget the eoln token - engine.Stream.UngetToken (token); - break; - } - - if (token.Type == ImapTokenType.OpenParen) { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - parenDepth++; - } - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - atom = (string) token.Value; - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - switch (atom.ToUpperInvariant ()) { - case "RELEVANCY": - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - results.Relevancy = new List (); - - do { - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - var score = ImapEngine.ParseNumber (token, true, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - if (score > 100) - throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - results.Relevancy.Add ((byte) score); - } while (true); - break; - case "MODSEQ": - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - results.ModSeq = ImapEngine.ParseNumber64 (token, false, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - break; - case "COUNT": - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - var count = ImapEngine.ParseNumber (token, false, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - results.Count = (int) count; - break; - case "MIN": - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - var min = ImapEngine.ParseNumber (token, true, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - results.Min = new UniqueId (ic.Folder.UidValidity, min); - break; - case "MAX": - 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); - break; - case "ALL": - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - - var uids = ImapEngine.ParseUidSet (token, ic.Folder.UidValidity, ImapEngine.GenericItemSyntaxErrorFormat, atom, token); - - results.Count = uids.Count; - results.UniqueIds = uids; - break; - default: - throw ImapEngine.UnexpectedToken (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ESEARCH", token); - } - - token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } while (true); - } - - async Task SearchAsync (string query, bool doAsync, CancellationToken cancellationToken) - { - if (query == null) - throw new ArgumentNullException (nameof (query)); - - query = query.Trim (); - - if (query.Length == 0) - throw new ArgumentException ("Cannot search using an empty query.", nameof (query)); - - CheckState (true, false); - - var command = "UID SEARCH " + query + "\r\n"; - var ic = new ImapCommand (Engine, cancellationToken, this, command); - if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0) - ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync); - - // Note: always register the untagged SEARCH handler because some servers will brokenly - // respond with "* SEARCH ..." instead of "* ESEARCH ..." even when using the extended - // search syntax. - ic.RegisterUntaggedHandler ("SEARCH", SearchMatchesAsync); - ic.UserData = new SearchResults (SortOrder.Ascending); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("SEARCH", ic); - - return (SearchResults) ic.UserData; - } - - /// - /// Search the folder for messages matching the specified query. - /// - /// - /// Sends a UID SEARCH command with the specified query passed directly to the IMAP server - /// with no interpretation by MailKit. This means that the query may contain any arguments that a - /// UID SEARCH command is allowed to have according to the IMAP specifications and any - /// extensions that are supported, including RETURN parameters. - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual SearchResults Search (string query, CancellationToken cancellationToken = default (CancellationToken)) - { - return SearchAsync (query, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously search the folder for messages matching the specified query. - /// - /// - /// Sends a UID SEARCH command with the specified query passed directly to the IMAP server - /// with no interpretation by MailKit. This means that the query may contain any arguments that a - /// UID SEARCH command is allowed to have according to the IMAP specifications and any - /// extensions that are supported, including RETURN parameters. - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task SearchAsync (string query, CancellationToken cancellationToken = default (CancellationToken)) - { - return SearchAsync (query, true, cancellationToken); - } - - async Task> SearchAsync (SearchQuery query, bool doAsync, bool retry, CancellationToken cancellationToken) - { - var args = new List (); - string charset; - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - CheckState (true, false); - - var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); - var expr = BuildQueryExpression (optimized, args, out charset); - var command = "UID SEARCH "; - - if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0) - command += "RETURN () "; - - if (charset != null && args.Count > 0 && !Engine.UTF8Enabled) - command += "CHARSET " + charset + " "; - - command += expr + "\r\n"; - - var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); - if ((Engine.Capabilities & ImapCapabilities.ESearch) != 0) - ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync); - - // Note: always register the untagged SEARCH handler because some servers will brokenly - // respond with "* SEARCH ..." instead of "* ESEARCH ..." even when using the extended - // search syntax. - ic.RegisterUntaggedHandler ("SEARCH", SearchMatchesAsync); - ic.UserData = new SearchResults (SortOrder.Ascending); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) { - if (retry && IsBadCharset (ic, charset)) - return await SearchAsync (query, doAsync, false, cancellationToken).ConfigureAwait (false); - - throw ImapCommandException.Create ("SEARCH", ic); - } - - return ((SearchResults) ic.UserData).UniqueIds; - } - - /// - /// Search the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Search (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - return SearchAsync (query, false, true, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously search the folder for messages matching the specified query. - /// - /// - /// The returned array of unique identifiers can be used with methods such as - /// . - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> SearchAsync (SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - return SearchAsync (query, true, true, cancellationToken); - } - - async Task SearchAsync (SearchOptions options, SearchQuery query, bool doAsync, bool retry, CancellationToken cancellationToken) - { - var args = new List (); - string charset; - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - CheckState (true, false); - - if ((Engine.Capabilities & ImapCapabilities.ESearch) == 0) - throw new NotSupportedException ("The IMAP server does not support the ESEARCH extension."); - - var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); - var expr = BuildQueryExpression (optimized, args, out charset); - var command = "UID SEARCH RETURN ("; - - if (options != SearchOptions.All && options != 0) { - if ((options & SearchOptions.All) != 0) - command += "ALL "; - if ((options & SearchOptions.Relevancy) != 0) - command += "RELEVANCY "; - if ((options & SearchOptions.Count) != 0) - command += "COUNT "; - if ((options & SearchOptions.Min) != 0) - command += "MIN "; - if ((options & SearchOptions.Max) != 0) - command += "MAX "; - command = command.TrimEnd (); - } - command += ") "; - - if (charset != null && args.Count > 0 && !Engine.UTF8Enabled) - command += "CHARSET " + charset + " "; - - command += expr + "\r\n"; - - var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); - ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync); - - // Note: always register the untagged SEARCH handler because some servers will brokenly - // respond with "* SEARCH ..." instead of "* ESEARCH ..." even when using the extended - // search syntax. - ic.RegisterUntaggedHandler ("SEARCH", SearchMatchesAsync); - ic.UserData = new SearchResults (); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) { - if (retry && IsBadCharset (ic, charset)) - return await SearchAsync (options, query, doAsync, false, cancellationToken).ConfigureAwait (false); - - throw ImapCommandException.Create ("SEARCH", ic); - } - - return (SearchResults) ic.UserData; - } - - /// - /// Search the folder for messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The IMAP server does not support the ESEARCH extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override SearchResults Search (SearchOptions options, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - return SearchAsync (options, query, false, true, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously search the folder for messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, - /// returning only the specified search results. - /// - /// The search results. - /// The search options. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The IMAP server does not support the ESEARCH extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SearchAsync (SearchOptions options, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - return SearchAsync (options, query, true, true, cancellationToken); - } - - async Task SortAsync (string query, bool doAsync, CancellationToken cancellationToken) - { - if (query == null) - throw new ArgumentNullException (nameof (query)); - - query = query.Trim (); - - if (query.Length == 0) - throw new ArgumentException ("Cannot sort using an empty query.", nameof (query)); - - if ((Engine.Capabilities & ImapCapabilities.Sort) == 0) - throw new NotSupportedException ("The IMAP server does not support the SORT extension."); - - CheckState (true, false); - - var command = "UID SORT " + query + "\r\n"; - var ic = new ImapCommand (Engine, cancellationToken, this, command); - if ((Engine.Capabilities & ImapCapabilities.ESort) != 0) - ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync); - ic.RegisterUntaggedHandler ("SORT", SearchMatchesAsync); - ic.UserData = new SearchResults (); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) - throw ImapCommandException.Create ("SORT", ic); - - return (SearchResults) ic.UserData; - } - - /// - /// Sort messages matching the specified query. - /// - /// - /// Sends a UID SORT command with the specified query passed directly to the IMAP server - /// with no interpretation by MailKit. This means that the query may contain any arguments that a - /// UID SORT command is allowed to have according to the IMAP specifications and any - /// extensions that are supported, including RETURN parameters. - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The IMAP server does not support the SORT extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual SearchResults Sort (string query, CancellationToken cancellationToken = default (CancellationToken)) - { - return SortAsync (query, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// Sends a UID SORT command with the specified query passed directly to the IMAP server - /// with no interpretation by MailKit. This means that the query may contain any arguments that a - /// UID SORT command is allowed to have according to the IMAP specifications and any - /// extensions that are supported, including RETURN parameters. - /// - /// An array of matching UIDs. - /// The search query. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The IMAP server does not support the SORT extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public virtual Task SortAsync (string query, CancellationToken cancellationToken = default (CancellationToken)) - { - return SortAsync (query, true, cancellationToken); - } - - async Task> SortAsync (SearchQuery query, IList orderBy, bool doAsync, bool retry, CancellationToken cancellationToken) - { - var args = new List (); - string charset; - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - if (orderBy == null) - throw new ArgumentNullException (nameof (orderBy)); - - if (orderBy.Count == 0) - throw new ArgumentException ("No sort order provided.", nameof (orderBy)); - - CheckState (true, false); - - if ((Engine.Capabilities & ImapCapabilities.Sort) == 0) - throw new NotSupportedException ("The IMAP server does not support the SORT extension."); - - var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); - var expr = BuildQueryExpression (optimized, args, out charset); - var order = BuildSortOrder (orderBy); - var command = "UID SORT "; - - if ((Engine.Capabilities & ImapCapabilities.ESort) != 0) - command += "RETURN () "; - - command += order + " " + (charset ?? "US-ASCII") + " " + expr + "\r\n"; - - var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); - if ((Engine.Capabilities & ImapCapabilities.ESort) != 0) - ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync); - else - ic.RegisterUntaggedHandler ("SORT", SearchMatchesAsync); - ic.UserData = new SearchResults (); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) { - if (retry && IsBadCharset (ic, charset)) - return await SortAsync (query, orderBy, doAsync, false, cancellationToken).ConfigureAwait (false); - - throw ImapCommandException.Create ("SORT", ic); - } - - return ((SearchResults) ic.UserData).UniqueIds; - } - - /// - /// Sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The server does not support the SORT extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Sort (SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)) - { - return SortAsync (query, orderBy, false, true, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// The returned array of unique identifiers will be sorted in the preferred order and - /// can be used with . - /// - /// An array of matching UIDs in the specified sort order. - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The server does not support the SORT extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> SortAsync (SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)) - { - return SortAsync (query, orderBy, true, true, cancellationToken); - } - - async Task SortAsync (SearchOptions options, SearchQuery query, IList orderBy, bool doAsync, bool retry, CancellationToken cancellationToken) - { - var args = new List (); - string charset; - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - if (orderBy == null) - throw new ArgumentNullException (nameof (orderBy)); - - if (orderBy.Count == 0) - throw new ArgumentException ("No sort order provided.", nameof (orderBy)); - - CheckState (true, false); - - if ((Engine.Capabilities & ImapCapabilities.ESort) == 0) - throw new NotSupportedException ("The IMAP server does not support the ESORT extension."); - - var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); - var expr = BuildQueryExpression (optimized, args, out charset); - var order = BuildSortOrder (orderBy); - - var command = "UID SORT RETURN ("; - if (options != SearchOptions.All && options != 0) { - if ((options & SearchOptions.All) != 0) - command += "ALL "; - if ((options & SearchOptions.Relevancy) != 0) - command += "RELEVANCY "; - if ((options & SearchOptions.Count) != 0) - command += "COUNT "; - if ((options & SearchOptions.Min) != 0) - command += "MIN "; - if ((options & SearchOptions.Max) != 0) - command += "MAX "; - command = command.TrimEnd (); - } - command += ") "; - - command += order + " " + (charset ?? "US-ASCII") + " " + expr + "\r\n"; - - var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); - ic.RegisterUntaggedHandler ("ESEARCH", ESearchMatchesAsync); - ic.UserData = new SearchResults (); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) { - if (retry && IsBadCharset (ic, charset)) - return await SortAsync (options, query, orderBy, doAsync, false, cancellationToken).ConfigureAwait (false); - - throw ImapCommandException.Create ("SORT", ic); - } - - return (SearchResults) ic.UserData; - } - - /// - /// Sort messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The IMAP server does not support the ESORT extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override SearchResults Sort (SearchOptions options, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)) - { - return SortAsync (options, query, orderBy, false, true, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously sort messages matching the specified query. - /// - /// - /// Searches the folder for messages matching the specified query, returning the search results in the specified sort order. - /// - /// The search results. - /// The search options. - /// The search query. - /// The sort order. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The IMAP server does not support the ESORT extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task SortAsync (SearchOptions options, SearchQuery query, IList orderBy, CancellationToken cancellationToken = default (CancellationToken)) - { - return SortAsync (options, query, orderBy, true, true, cancellationToken); - } - - static async Task ThreadMatchesAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - ic.UserData = await ImapUtils.ParseThreadsAsync (engine, ic.Folder.UidValidity, doAsync, ic.CancellationToken).ConfigureAwait (false); - } - - async Task> ThreadAsync (ThreadingAlgorithm algorithm, SearchQuery query, bool doAsync, bool retry, CancellationToken cancellationToken) - { - var method = algorithm.ToString ().ToUpperInvariant (); - var args = new List (); - string charset; - - if ((Engine.Capabilities & ImapCapabilities.Thread) == 0) - throw new NotSupportedException ("The IMAP server does not support the THREAD extension."); - - if (!Engine.ThreadingAlgorithms.Contains (algorithm)) - throw new ArgumentOutOfRangeException (nameof (algorithm), "The specified threading algorithm is not supported."); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - CheckState (true, false); - - var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); - var expr = BuildQueryExpression (optimized, args, out charset); - var command = "UID THREAD " + method + " " + (charset ?? "US-ASCII") + " "; - - command += expr + "\r\n"; - - var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); - ic.RegisterUntaggedHandler ("THREAD", ThreadMatchesAsync); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) { - if (retry && IsBadCharset (ic, charset)) - return await ThreadAsync (algorithm, query, doAsync, false, cancellationToken).ConfigureAwait (false); - - throw ImapCommandException.Create ("THREAD", ic); - } - - var threads = (IList) ic.UserData; - - if (threads == null) - return new MessageThread[0]; - - return threads; - } - - /// - /// Thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - /// - /// is not supported. - /// - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The server does not support the THREAD extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Thread (ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - return ThreadAsync (algorithm, query, false, true, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - /// - /// is not supported. - /// - /// - /// is null. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The server does not support the THREAD extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> ThreadAsync (ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - return ThreadAsync (algorithm, query, true, true, cancellationToken); - } - - async Task> ThreadAsync (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, bool doAsync, bool retry, CancellationToken cancellationToken) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if ((Engine.Capabilities & ImapCapabilities.Thread) == 0) - throw new NotSupportedException ("The IMAP server does not support the THREAD extension."); - - if (!Engine.ThreadingAlgorithms.Contains (algorithm)) - throw new ArgumentOutOfRangeException (nameof (algorithm), "The specified threading algorithm is not supported."); - - if (query == null) - throw new ArgumentNullException (nameof (query)); - - CheckState (true, false); - - if (uids.Count == 0) - return new MessageThread[0]; - - var method = algorithm.ToString ().ToUpperInvariant (); - var set = UniqueIdSet.ToString (uids); - var args = new List (); - string charset; - - var optimized = query.Optimize (new ImapSearchQueryOptimizer ()); - var expr = BuildQueryExpression (optimized, args, out charset); - var command = "UID THREAD " + method + " " + (charset ?? "US-ASCII") + " "; - - command += "UID " + set + " " + expr + "\r\n"; - - var ic = new ImapCommand (Engine, cancellationToken, this, command, args.ToArray ()); - ic.RegisterUntaggedHandler ("THREAD", ThreadMatchesAsync); - - Engine.QueueCommand (ic); - - await Engine.RunAsync (ic, doAsync).ConfigureAwait (false); - - ProcessResponseCodes (ic, null); - - if (ic.Response != ImapCommandResponse.Ok) { - if (retry && IsBadCharset (ic, charset)) - return await ThreadAsync (uids, algorithm, query, doAsync, false, cancellationToken).ConfigureAwait (false); - - throw ImapCommandException.Create ("THREAD", ic); - } - - var threads = (IList) ic.UserData; - - if (threads == null) - return new MessageThread[0]; - - return threads; - } - - /// - /// Thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The subset of UIDs - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - /// - /// is not supported. - /// - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The server does not support the THREAD extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override IList Thread (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - return ThreadAsync (uids, algorithm, query, false, true, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously thread the messages in the folder that match the search query using the specified threading algorithm. - /// - /// - /// The can be used with methods such as - /// . - /// - /// An array of message threads. - /// The subset of UIDs - /// The threading algorithm to use. - /// The search query. - /// The cancellation token. - /// - /// is not supported. - /// - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// One or more of the is invalid. - /// - /// - /// One or more search terms in the are not supported by the IMAP server. - /// -or- - /// The server does not support the THREAD extension. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The is not currently open. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The server's response contained unexpected tokens. - /// - /// - /// The server replied with a NO or BAD response. - /// - public override Task> ThreadAsync (IList uids, ThreadingAlgorithm algorithm, SearchQuery query, CancellationToken cancellationToken = default (CancellationToken)) - { - return ThreadAsync (uids, algorithm, query, true, true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Imap/ImapImplementation.cs b/src/MailKit/Net/Imap/ImapImplementation.cs deleted file mode 100644 index ee3227e..0000000 --- a/src/MailKit/Net/Imap/ImapImplementation.cs +++ /dev/null @@ -1,217 +0,0 @@ -// -// ImapImplementation.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Collections.Generic; - -namespace MailKit.Net.Imap { - /// - /// The details of an IMAP client or server implementation. - /// - /// - /// Allows an IMAP client and server to share their implementation details - /// with each other for the purposes of debugging. - /// - /// - /// - /// - public class ImapImplementation - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// - /// - /// - public ImapImplementation () - { - Properties = new Dictionary (); - } - - string GetProperty (string property) - { - string value; - - Properties.TryGetValue (property, out value); - - return value; - } - - /// - /// Get the identification properties. - /// - /// - /// Gets the dictionary of raw identification properties. - /// - /// - /// - /// - /// The properties. - public Dictionary Properties { - get; private set; - } - - /// - /// Get or set the name of the program. - /// - /// - /// Gets or sets the name of the program. - /// - /// - /// - /// - /// The program name. - public string Name { - get { return GetProperty ("name"); } - set { Properties["name"] = value; } - } - - /// - /// Get or set the version of the program. - /// - /// - /// Gets or sets the version of the program. - /// - /// - /// - /// - /// The program version. - public string Version { - get { return GetProperty ("version"); } - set { Properties["version"] = value; } - } - - /// - /// Get or set the name of the operating system. - /// - /// - /// Gets or sets the name of the operating system. - /// - /// The name of the operation system. - public string OS { - get { return GetProperty ("os"); } - set { Properties["os"] = value; } - } - - /// - /// Get or set the version of the operating system. - /// - /// - /// Gets or sets the version of the operating system. - /// - /// The version of the operation system. - public string OSVersion { - get { return GetProperty ("os-version"); } - set { Properties["os-version"] = value; } - } - - /// - /// Get or set the name of the vendor. - /// - /// - /// Gets or sets the name of the vendor. - /// - /// The name of the vendor. - public string Vendor { - get { return GetProperty ("vendor"); } - set { Properties["vendor"] = value; } - } - - /// - /// Get or set the support URL. - /// - /// - /// Gets or sets the support URL. - /// - /// The support URL. - public string SupportUrl { - get { return GetProperty ("support-url"); } - set { Properties["support-url"] = value; } - } - - /// - /// Get or set the postal address of the vendor. - /// - /// - /// Gets or sets the postal address of the vendor. - /// - /// The postal address. - public string Address { - get { return GetProperty ("address"); } - set { Properties["address"] = value; } - } - - /// - /// Get or set the release date of the program. - /// - /// - /// Gets or sets the release date of the program. - /// - /// The release date. - public string ReleaseDate { - get { return GetProperty ("date"); } - set { Properties["date"] = value; } - } - - /// - /// Get or set the command used to start the program. - /// - /// - /// Gets or sets the command used to start the program. - /// - /// The command used to start the program. - public string Command { - get { return GetProperty ("command"); } - set { Properties["command"] = value; } - } - - /// - /// Get or set the command-line arguments used to start the program. - /// - /// - /// Gets or sets the command-line arguments used to start the program. - /// - /// The command-line arguments used to start the program. - public string Arguments { - get { return GetProperty ("arguments"); } - set { Properties["arguments"] = value; } - } - - /// - /// Get or set the environment variables available to the program. - /// - /// - /// Get or set the environment variables available to the program. - /// - /// The environment variables. - public string Environment { - get { return GetProperty ("environment"); } - set { Properties["environment"] = value; } - } - } -} diff --git a/src/MailKit/Net/Imap/ImapProtocolException.cs b/src/MailKit/Net/Imap/ImapProtocolException.cs deleted file mode 100644 index a1aad3d..0000000 --- a/src/MailKit/Net/Imap/ImapProtocolException.cs +++ /dev/null @@ -1,109 +0,0 @@ -// -// ImapException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit.Net.Imap { - /// - /// An IMAP protocol exception. - /// - /// - /// The exception that is thrown when there is an error communicating with an IMAP server. An - /// is typically fatal and requires the - /// to be reconnected. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class ImapProtocolException : ProtocolException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the serialized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected ImapProtocolException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// An inner exception. - public ImapProtocolException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public ImapProtocolException (string message) : base (message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public ImapProtocolException () - { - } - - /// - /// Gets or sets whether or not this exception was thrown due to an unexpected token. - /// - /// - /// Gets or sets whether or not this exception was thrown due to an unexpected token. - /// - /// true if an unexpected token was encountered; otherwise, false. - internal bool UnexpectedToken { - get; set; - } - } -} diff --git a/src/MailKit/Net/Imap/ImapResponseCode.cs b/src/MailKit/Net/Imap/ImapResponseCode.cs deleted file mode 100644 index 4d0b2d5..0000000 --- a/src/MailKit/Net/Imap/ImapResponseCode.cs +++ /dev/null @@ -1,373 +0,0 @@ -// -// ImapResponseCode.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Net.Imap { - enum ImapResponseCodeType : byte { - Alert, - BadCharset, - Capability, - NewName, - Parse, - PermanentFlags, - ReadOnly, - ReadWrite, - TryCreate, - UidNext, - UidValidity, - Unseen, - - // RESP-CODES introduced in rfc2221: - Referral, - - // RESP-CODES introduced in rfc3516, - UnknownCte, - - // RESP-CODES introduced in rfc4315: - AppendUid, - CopyUid, - UidNotSticky, - - // RESP-CODES introduced in rfc4467: - UrlMech, - - // RESP-CODES introduced in rfc4469: - BadUrl, - TooBig, - - // RESP-CODES introduced in rfc4551: - HighestModSeq, - Modified, - NoModSeq, - - // RESP-CODES introduced in rfc4978: - CompressionActive, - - // RESP-CODES introduced in rfc5162: - Closed, - - // RESP-CODES introduced in rfc5182: - NotSaved, - - // RESP-CODES introduced in rfc5255: - BadComparator, - - // RESP-CODES introduced in rfc5257: - Annotate, - Annotations, - - // RESP-CODES introduced in rfc5259: - MaxConvertMessages, - MaxConvertParts, - TempFail, - - // RESP-CODES introduced in rfc5267: - NoUpdate, - - // RESP-CODES introduced in rfc5464: - Metadata, - - // RESP-CODES introduced in rfc5465: - NotificationOverflow, - BadEvent, - - // RESP-CODES introduced in rfc5466: - UndefinedFilter, - - // RESP-CODES introduced in rfc5530: - Unavailable, - AuthenticationFailed, - AuthorizationFailed, - Expired, - PrivacyRequired, - ContactAdmin, - NoPerm, - InUse, - ExpungeIssued, - Corruption, - ServerBug, - ClientBug, - CanNot, - Limit, - OverQuota, - AlreadyExists, - NonExistent, - - // RESP-CODES introduced in rfc6154: - UseAttr, - - // RESP-CODES introduced in rfc8474: - MailboxId, - - Unknown = 255 - } - - class ImapResponseCode - { - public readonly ImapResponseCodeType Type; - public bool IsTagged, IsError; - public string Message; - - internal ImapResponseCode (ImapResponseCodeType type, bool isError) - { - IsError = isError; - Type = type; - } - - public static ImapResponseCode Create (ImapResponseCodeType type) - { - switch (type) { - case ImapResponseCodeType.Alert: return new ImapResponseCode (type, false); - case ImapResponseCodeType.BadCharset: return new ImapResponseCode (type, true); - case ImapResponseCodeType.Capability: return new ImapResponseCode (type, false); - case ImapResponseCodeType.NewName: return new NewNameResponseCode (type); - case ImapResponseCodeType.Parse: return new ImapResponseCode (type, true); - case ImapResponseCodeType.PermanentFlags: return new PermanentFlagsResponseCode (type); - case ImapResponseCodeType.ReadOnly: return new ImapResponseCode (type, false); - case ImapResponseCodeType.ReadWrite: return new ImapResponseCode (type, false); - case ImapResponseCodeType.TryCreate: return new ImapResponseCode (type, true); - case ImapResponseCodeType.UidNext: return new UidNextResponseCode (type); - case ImapResponseCodeType.UidValidity: return new UidValidityResponseCode (type); - case ImapResponseCodeType.Unseen: return new UnseenResponseCode (type); - case ImapResponseCodeType.Referral: return new ImapResponseCode (type, false); - case ImapResponseCodeType.UnknownCte: return new ImapResponseCode (type, true); - case ImapResponseCodeType.AppendUid: return new AppendUidResponseCode (type); - case ImapResponseCodeType.CopyUid: return new CopyUidResponseCode (type); - case ImapResponseCodeType.UidNotSticky: return new ImapResponseCode (type, false); - case ImapResponseCodeType.UrlMech: return new ImapResponseCode (type, false); - case ImapResponseCodeType.BadUrl: return new BadUrlResponseCode (type); - case ImapResponseCodeType.TooBig: return new ImapResponseCode (type, true); - case ImapResponseCodeType.HighestModSeq: return new HighestModSeqResponseCode (type); - case ImapResponseCodeType.Modified: return new ModifiedResponseCode (type); - case ImapResponseCodeType.NoModSeq: return new ImapResponseCode (type, false); - case ImapResponseCodeType.CompressionActive: return new ImapResponseCode (type, true); - case ImapResponseCodeType.Closed: return new ImapResponseCode (type, false); - case ImapResponseCodeType.NotSaved: return new ImapResponseCode (type, true); - case ImapResponseCodeType.BadComparator: return new ImapResponseCode (type, true); - case ImapResponseCodeType.Annotate: return new AnnotateResponseCode (type); - case ImapResponseCodeType.Annotations: return new AnnotationsResponseCode (type); - case ImapResponseCodeType.MaxConvertMessages: return new MaxConvertResponseCode (type); - case ImapResponseCodeType.MaxConvertParts: return new MaxConvertResponseCode (type); - case ImapResponseCodeType.TempFail: return new ImapResponseCode (type, true); - case ImapResponseCodeType.NoUpdate: return new NoUpdateResponseCode (type); - case ImapResponseCodeType.Metadata: return new MetadataResponseCode (type); - case ImapResponseCodeType.NotificationOverflow: return new ImapResponseCode (type, false); - case ImapResponseCodeType.BadEvent: return new ImapResponseCode (type, true); - case ImapResponseCodeType.UndefinedFilter: return new UndefinedFilterResponseCode (type); - case ImapResponseCodeType.Unavailable: return new ImapResponseCode (type, true); - case ImapResponseCodeType.AuthenticationFailed: return new ImapResponseCode (type, true); - case ImapResponseCodeType.AuthorizationFailed: return new ImapResponseCode (type, true); - case ImapResponseCodeType.Expired: return new ImapResponseCode (type, true); - case ImapResponseCodeType.PrivacyRequired: return new ImapResponseCode (type, true); - case ImapResponseCodeType.ContactAdmin: return new ImapResponseCode (type, true); - case ImapResponseCodeType.NoPerm: return new ImapResponseCode (type, true); - case ImapResponseCodeType.InUse: return new ImapResponseCode (type, true); - case ImapResponseCodeType.ExpungeIssued: return new ImapResponseCode (type, true); - case ImapResponseCodeType.Corruption: return new ImapResponseCode (type, true); - case ImapResponseCodeType.ServerBug: return new ImapResponseCode (type, true); - case ImapResponseCodeType.ClientBug: return new ImapResponseCode (type, true); - case ImapResponseCodeType.CanNot: return new ImapResponseCode (type, true); - case ImapResponseCodeType.Limit: return new ImapResponseCode (type, true); - case ImapResponseCodeType.OverQuota: return new ImapResponseCode (type, true); - case ImapResponseCodeType.AlreadyExists: return new ImapResponseCode (type, true); - case ImapResponseCodeType.NonExistent: return new ImapResponseCode (type, true); - case ImapResponseCodeType.UseAttr: return new ImapResponseCode (type, true); - case ImapResponseCodeType.MailboxId: return new MailboxIdResponseCode (type); - default: return new ImapResponseCode (type, true); - } - } - } - - class NewNameResponseCode : ImapResponseCode - { - public string OldName; - public string NewName; - - internal NewNameResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } - - class PermanentFlagsResponseCode : ImapResponseCode - { - public MessageFlags Flags; - - internal PermanentFlagsResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } - - class UidNextResponseCode : ImapResponseCode - { - public UniqueId Uid; - - internal UidNextResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } - - class UidValidityResponseCode : ImapResponseCode - { - public uint UidValidity; - - internal UidValidityResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } - - class UnseenResponseCode : ImapResponseCode - { - public int Index; - - internal UnseenResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } - - class AppendUidResponseCode : UidValidityResponseCode - { - public UniqueIdSet UidSet; - - internal AppendUidResponseCode (ImapResponseCodeType type) : base (type) - { - } - } - - class CopyUidResponseCode : UidValidityResponseCode - { - public UniqueIdSet SrcUidSet, DestUidSet; - - internal CopyUidResponseCode (ImapResponseCodeType type) : base (type) - { - } - } - - class BadUrlResponseCode : ImapResponseCode - { - public string BadUrl; - - internal BadUrlResponseCode (ImapResponseCodeType type) : base (type, true) - { - } - } - - class HighestModSeqResponseCode : ImapResponseCode - { - public ulong HighestModSeq; - - internal HighestModSeqResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } - - class ModifiedResponseCode : ImapResponseCode - { - public UniqueIdSet UidSet; - - internal ModifiedResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } - - class MaxConvertResponseCode : ImapResponseCode - { - public uint MaxConvert; - - internal MaxConvertResponseCode (ImapResponseCodeType type) : base (type, true) - { - } - } - - class NoUpdateResponseCode : ImapResponseCode - { - public string Tag; - - internal NoUpdateResponseCode (ImapResponseCodeType type) : base (type, true) - { - } - } - - enum AnnotateResponseCodeSubType - { - TooBig, - TooMany - } - - class AnnotateResponseCode : ImapResponseCode - { - public AnnotateResponseCodeSubType SubType; - - internal AnnotateResponseCode (ImapResponseCodeType type) : base (type, true) - { - } - } - - class AnnotationsResponseCode : ImapResponseCode - { - public AnnotationAccess Access; - public AnnotationScope Scopes; - public uint MaxSize; - - internal AnnotationsResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } - - enum MetadataResponseCodeSubType - { - LongEntries, - MaxSize, - TooMany, - NoPrivate - } - - class MetadataResponseCode : ImapResponseCode - { - public MetadataResponseCodeSubType SubType; - public uint Value; - - internal MetadataResponseCode (ImapResponseCodeType type) : base (type, true) - { - } - } - - class UndefinedFilterResponseCode : ImapResponseCode - { - public string Name; - - internal UndefinedFilterResponseCode (ImapResponseCodeType type) : base (type, true) - { - } - } - - class MailboxIdResponseCode : ImapResponseCode - { - public string MailboxId; - - internal MailboxIdResponseCode (ImapResponseCodeType type) : base (type, false) - { - } - } -} diff --git a/src/MailKit/Net/Imap/ImapSearchQueryOptimizer.cs b/src/MailKit/Net/Imap/ImapSearchQueryOptimizer.cs deleted file mode 100644 index 4cba40e..0000000 --- a/src/MailKit/Net/Imap/ImapSearchQueryOptimizer.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// ImapSearchQueryOptimizer.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MailKit.Search; - -namespace MailKit.Net.Imap { - class ImapSearchQueryOptimizer : ISearchQueryOptimizer - { - #region ISearchQueryOptimizer implementation - - public SearchQuery Reduce (SearchQuery expr) - { - if (expr.Term == SearchTerm.And) { - var and = (BinarySearchQuery) expr; - - if (and.Left.Term == SearchTerm.All) - return and.Right.Optimize (this); - - if (and.Right.Term == SearchTerm.All) - return and.Left.Optimize (this); - } else if (expr.Term == SearchTerm.Or) { - var or = (BinarySearchQuery) expr; - - if (or.Left.Term == SearchTerm.All) - return SearchQuery.All; - - if (or.Right.Term == SearchTerm.All) - return SearchQuery.All; - } else if (expr.Term == SearchTerm.Not) { - var unary = (UnarySearchQuery) expr; - - switch (unary.Operand.Term) { - case SearchTerm.Not: return ((UnarySearchQuery) unary.Operand).Operand.Optimize (this); - case SearchTerm.NotAnswered: return SearchQuery.Answered; - case SearchTerm.Answered: return SearchQuery.NotAnswered; - case SearchTerm.NotDeleted: return SearchQuery.Deleted; - case SearchTerm.Deleted: return SearchQuery.NotDeleted; - case SearchTerm.NotDraft: return SearchQuery.Draft; - case SearchTerm.Draft: return SearchQuery.NotDraft; - case SearchTerm.NotFlagged: return SearchQuery.Flagged; - case SearchTerm.Flagged: return SearchQuery.NotFlagged; - case SearchTerm.NotRecent: return SearchQuery.Recent; - case SearchTerm.Recent: return SearchQuery.NotRecent; - case SearchTerm.NotSeen: return SearchQuery.Seen; - case SearchTerm.Seen: return SearchQuery.NotSeen; - } - - if (unary.Operand.Term == SearchTerm.Keyword) - return new TextSearchQuery (SearchTerm.NotKeyword, ((TextSearchQuery) unary.Operand).Text); - - if (unary.Operand.Term == SearchTerm.NotKeyword) - return new TextSearchQuery (SearchTerm.Keyword, ((TextSearchQuery) unary.Operand).Text); - } - - return expr; - } - - #endregion - } -} diff --git a/src/MailKit/Net/Imap/ImapStream.cs b/src/MailKit/Net/Imap/ImapStream.cs deleted file mode 100644 index e2df9c0..0000000 --- a/src/MailKit/Net/Imap/ImapStream.cs +++ /dev/null @@ -1,1190 +0,0 @@ -// -// ImapStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Globalization; -using System.Threading.Tasks; - -using MimeKit.IO; - -using Buffer = System.Buffer; -using SslStream = MailKit.Net.SslStream; -using NetworkStream = MailKit.Net.NetworkStream; - -namespace MailKit.Net.Imap { - /// - /// An enumeration of the possible IMAP streaming modes. - /// - /// - /// Normal operation is done in the mode, - /// but when reading literal string data, the - /// mode should be used. - /// - enum ImapStreamMode { - /// - /// Reads 1 token at a time. - /// - Token, - - /// - /// Reads literal string data. - /// - Literal - } - - class ImapStream : Stream, ICancellableStream - { - public const string AtomSpecials = "(){%*\\\"\n"; - public const string DefaultSpecials = "[]" + AtomSpecials; - const int ReadAheadSize = 128; - const int BlockSize = 4096; - const int PadSize = 4; - - static readonly Encoding Latin1; - static readonly Encoding UTF8; - - // I/O buffering - readonly byte[] input = new byte[ReadAheadSize + BlockSize + PadSize]; - const int inputStart = ReadAheadSize; - int inputIndex = ReadAheadSize; - int inputEnd = ReadAheadSize; - - readonly byte[] output = new byte[BlockSize]; - int outputIndex; - - readonly IProtocolLogger logger; - int literalDataLeft; - ImapToken nextToken; - bool disposed; - - static ImapStream () - { - UTF8 = Encoding.GetEncoding (65001, new EncoderExceptionFallback (), new DecoderExceptionFallback ()); - - try { - Latin1 = Encoding.GetEncoding (28591); - } catch (NotSupportedException) { - Latin1 = Encoding.GetEncoding (1252); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The underlying network stream. - /// The protocol logger. - public ImapStream (Stream source, IProtocolLogger protocolLogger) - { - logger = protocolLogger; - IsConnected = true; - Stream = source; - } - - /// - /// Get or sets the underlying network stream. - /// - /// - /// Gets or sets the underlying network stream. - /// - /// The underlying network stream. - public Stream Stream { - get; internal set; - } - - /// - /// Get or sets the mode used for reading. - /// - /// - /// Gets or sets the mode used for reading. - /// - /// The mode. - public ImapStreamMode Mode { - get; set; - } - - /// - /// Get the length of the literal. - /// - /// - /// Gets the length of the literal. - /// - /// The length of the literal. - public int LiteralLength { - get { return literalDataLeft; } - internal set { literalDataLeft = value; } - } - - /// - /// Get whether or not the stream is connected. - /// - /// - /// Gets whether or not the stream is connected. - /// - /// true if the stream is connected; otherwise, false. - public bool IsConnected { - get; private set; - } - - /// - /// Get whether the stream supports reading. - /// - /// - /// Gets whether the stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return Stream.CanRead; } - } - - /// - /// Get whether the stream supports writing. - /// - /// - /// Gets whether the stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return Stream.CanWrite; } - } - - /// - /// Get whether the stream supports seeking. - /// - /// - /// Gets whether the stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return false; } - } - - /// - /// Get whether the stream supports I/O timeouts. - /// - /// - /// Gets whether the stream supports I/O timeouts. - /// - /// true if the stream supports I/O timeouts; otherwise, false. - public override bool CanTimeout { - get { return Stream.CanTimeout; } - } - - /// - /// Get or set a value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// - /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// A value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// The read timeout. - public override int ReadTimeout { - get { return Stream.ReadTimeout; } - set { Stream.ReadTimeout = value; } - } - - /// - /// Get or set a value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// - /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// A value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// The write timeout. - public override int WriteTimeout { - get { return Stream.WriteTimeout; } - set { Stream.WriteTimeout = value; } - } - - /// - /// Get or set the position within the current stream. - /// - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return Stream.Position; } - set { throw new NotSupportedException (); } - } - - /// - /// Get the length of the stream, in bytes. - /// - /// - /// Gets the length of the stream, in bytes. - /// - /// A long value representing the length of the stream in bytes. - /// The length of the stream. - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Length { - get { return Stream.Length; } - } - - async Task ReadAheadAsync (int atleast, bool doAsync, CancellationToken cancellationToken) - { - int left = inputEnd - inputIndex; - - if (left >= atleast) - return left; - - int start = inputStart; - int end = inputEnd; - int nread; - - if (left > 0) { - int index = inputIndex; - - // attempt to align the end of the remaining input with ReadAheadSize - if (index >= start) { - start -= Math.Min (ReadAheadSize, left); - Buffer.BlockCopy (input, index, input, start, left); - index = start; - start += left; - } else if (index > 0) { - int shift = Math.Min (index, end - start); - Buffer.BlockCopy (input, index, input, index - shift, left); - index -= shift; - start = index + left; - } else { - // we can't shift... - start = end; - } - - inputIndex = index; - inputEnd = start; - } else { - inputIndex = start; - inputEnd = start; - } - - end = input.Length - PadSize; - - try { - var network = Stream as NetworkStream; - - cancellationToken.ThrowIfCancellationRequested (); - - if (doAsync) { - nread = await Stream.ReadAsync (input, start, end - start, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectRead, cancellationToken); - nread = Stream.Read (input, start, end - start); - } - - if (nread > 0) { - logger.LogServer (input, start, nread); - inputEnd += nread; - } else { - throw new ImapProtocolException ("The IMAP server has unexpectedly disconnected."); - } - - if (network == null) - cancellationToken.ThrowIfCancellationRequested (); - } catch { - IsConnected = false; - throw; - } - - return inputEnd - inputIndex; - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (ImapStream)); - } - - async Task ReadAsync (byte[] buffer, int offset, int count, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - if (Mode != ImapStreamMode.Literal) - return 0; - - count = Math.Min (count, literalDataLeft); - - int length = inputEnd - inputIndex; - int n; - - if (length < count && length <= ReadAheadSize) - await ReadAheadAsync (BlockSize, doAsync, cancellationToken).ConfigureAwait (false); - - length = inputEnd - inputIndex; - n = Math.Min (count, length); - - Buffer.BlockCopy (input, inputIndex, buffer, offset, n); - literalDataLeft -= n; - inputIndex += n; - - if (literalDataLeft == 0) - Mode = ImapStreamMode.Token; - - return n; - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream is in token mode (see ). - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public int Read (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return ReadAsync (buffer, offset, count, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream is in token mode (see ). - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - return Read (buffer, offset, count, CancellationToken.None); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return ReadAsync (buffer, offset, count, true, cancellationToken); - } - - static bool IsAtom (byte c, string specials) - { - return !IsCtrl (c) && !IsWhiteSpace (c) && specials.IndexOf ((char) c) == -1; - } - - static bool IsCtrl (byte c) - { - return c <= 0x1f || c == 0x7f; - } - - static bool IsWhiteSpace (byte c) - { - return c == (byte) ' ' || c == (byte) '\t' || c == (byte) '\r'; - } - - async Task ReadQuotedStringTokenAsync (bool doAsync, CancellationToken cancellationToken) - { - bool escaped = false; - - // skip over the opening '"' - inputIndex++; - - using (var memory = new MemoryStream ()) { - do { - while (inputIndex < inputEnd) { - if (input[inputIndex] == (byte) '"' && !escaped) - break; - - if (input[inputIndex] == (byte) '\\' && !escaped) { - escaped = true; - } else { - memory.WriteByte (input[inputIndex]); - escaped = false; - } - - inputIndex++; - } - - if (inputIndex + 1 < inputEnd) { - // skip over closing '"' - inputIndex++; - - // Note: Some IMAP servers do not properly escape double-quotes inside - // of a qstring token and so, as an attempt at working around this - // problem, check that the closing '"' character is not immediately - // followed by any character that we would expect immediately following - // a qstring token. - // - // See https://github.com/jstedfast/MailKit/issues/485 for details. - if ("]) \r\n".IndexOf ((char) input[inputIndex]) != -1) - break; - - memory.WriteByte ((byte) '"'); - continue; - } - - await ReadAheadAsync (2, doAsync, cancellationToken).ConfigureAwait (false); - } while (true); - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - var buffer = memory.GetBuffer (); -#else - var buffer = memory.ToArray (); -#endif - int length = (int) memory.Length; - - return new ImapToken (ImapTokenType.QString, Encoding.UTF8.GetString (buffer, 0, length)); - } - } - - async Task ReadAtomStringAsync (bool flag, string specials, bool doAsync, CancellationToken cancellationToken) - { - using (var memory = new MemoryStream ()) { - do { - input[inputEnd] = (byte) '\n'; - - if (flag && memory.Length == 0 && input[inputIndex] == (byte) '*') { - // this is a special wildcard flag - inputIndex++; - return "*"; - } - - while (IsAtom (input[inputIndex], specials)) - memory.WriteByte (input[inputIndex++]); - - if (inputIndex < inputEnd) - break; - - await ReadAheadAsync (1, doAsync, cancellationToken).ConfigureAwait (false); - } while (true); - - var count = (int) memory.Length; -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - var buf = memory.GetBuffer (); -#else - var buf = memory.ToArray (); -#endif - - try { - return UTF8.GetString (buf, 0, count); - } catch (DecoderFallbackException) { - return Latin1.GetString (buf, 0, count); - } - } - } - - async Task ReadAtomTokenAsync (string specials, bool doAsync, CancellationToken cancellationToken) - { - var atom = await ReadAtomStringAsync (false, specials, doAsync, cancellationToken).ConfigureAwait (false); - - return atom == "NIL" ? new ImapToken (ImapTokenType.Nil, atom) : new ImapToken (ImapTokenType.Atom, atom); - } - - async Task ReadFlagTokenAsync (string specials, bool doAsync, CancellationToken cancellationToken) - { - inputIndex++; - - var flag = "\\" + await ReadAtomStringAsync (true, specials, doAsync, cancellationToken).ConfigureAwait (false); - - return new ImapToken (ImapTokenType.Flag, flag); - } - - async Task ReadLiteralTokenAsync (bool doAsync, CancellationToken cancellationToken) - { - var builder = new StringBuilder (); - - // skip over the '{' - inputIndex++; - - do { - input[inputEnd] = (byte) '}'; - - while (input[inputIndex] != (byte) '}' && input[inputIndex] != '+') - builder.Append ((char) input[inputIndex++]); - - if (inputIndex < inputEnd) - break; - - await ReadAheadAsync (1, doAsync, cancellationToken).ConfigureAwait (false); - } while (true); - - if (input[inputIndex] == (byte) '+') - inputIndex++; - - // technically, we need "}\r\n", but in order to be more lenient, we'll accept "}\n" - await ReadAheadAsync (2, doAsync, cancellationToken).ConfigureAwait (false); - - if (input[inputIndex] != (byte) '}') { - // PROTOCOL ERROR... but maybe we can work around it? - do { - input[inputEnd] = (byte) '}'; - - while (input[inputIndex] != (byte) '}') - inputIndex++; - - if (inputIndex < inputEnd) - break; - - await ReadAheadAsync (1, doAsync, cancellationToken).ConfigureAwait (false); - } while (true); - } - - // skip over the '}' - inputIndex++; - - // read until we get a new line... - do { - input[inputEnd] = (byte) '\n'; - - while (input[inputIndex] != (byte) '\n') - inputIndex++; - - if (inputIndex < inputEnd) - break; - - await ReadAheadAsync (1, doAsync, cancellationToken).ConfigureAwait (false); - } while (true); - - // skip over the '\n' - inputIndex++; - - if (!int.TryParse (builder.ToString (), NumberStyles.None, CultureInfo.InvariantCulture, out literalDataLeft) || literalDataLeft < 0) - return new ImapToken (ImapTokenType.Error, builder.ToString ()); - - Mode = ImapStreamMode.Literal; - - return new ImapToken (ImapTokenType.Literal, literalDataLeft); - } - - internal async Task ReadTokenAsync (string specials, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (nextToken != null) { - var token = nextToken; - nextToken = null; - return token; - } - - input[inputEnd] = (byte) '\n'; - - // skip over white space between tokens... - do { - while (IsWhiteSpace (input[inputIndex])) - inputIndex++; - - if (inputIndex < inputEnd) - break; - - await ReadAheadAsync (1, doAsync, cancellationToken).ConfigureAwait (false); - - input[inputEnd] = (byte) '\n'; - } while (true); - - char c = (char) input[inputIndex]; - - if (c == '"') - return await ReadQuotedStringTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (c == '{') - return await ReadLiteralTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (c == '\\') - return await ReadFlagTokenAsync (specials, doAsync, cancellationToken).ConfigureAwait (false); - - if (IsAtom (input[inputIndex], specials)) - return await ReadAtomTokenAsync (specials, doAsync, cancellationToken).ConfigureAwait (false); - - // special character token - inputIndex++; - - return new ImapToken ((ImapTokenType) c, c); - } - - internal Task ReadTokenAsync (bool doAsync, CancellationToken cancellationToken) - { - return ReadTokenAsync (DefaultSpecials, doAsync, cancellationToken); - } - - /// - /// Reads the next available token from the stream. - /// - /// The token. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public ImapToken ReadToken (CancellationToken cancellationToken) - { - return ReadTokenAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously reads the next available token from the stream. - /// - /// The token. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task ReadTokenAsync (CancellationToken cancellationToken) - { - return ReadTokenAsync (true, cancellationToken); - } - - /// - /// Ungets a token. - /// - /// The token. - public void UngetToken (ImapToken token) - { - if (token == null) - throw new ArgumentNullException (nameof (token)); - - nextToken = token; - } - - async Task ReadLineAsync (Stream ostream, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (inputIndex == inputEnd) - await ReadAheadAsync (1, doAsync, cancellationToken).ConfigureAwait (false); - - unsafe { - fixed (byte* inbuf = input) { - byte* start, inptr, inend; - int offset = inputIndex; - int count; - - start = inbuf + inputIndex; - inend = inbuf + inputEnd; - *inend = (byte) '\n'; - inptr = start; - - // FIXME: use SIMD to optimize this - while (*inptr != (byte) '\n') - inptr++; - - inputIndex = (int) (inptr - inbuf); - count = (int) (inptr - start); - - if (inptr == inend) { - ostream.Write (input, offset, count); - return false; - } - - // consume the '\n' - inputIndex++; - count++; - - ostream.Write (input, offset, count); - - return true; - } - } - } - - /// - /// Reads a single line of input from the stream. - /// - /// - /// This method should be called in a loop until it returns true. - /// - /// true, if reading the line is complete, false otherwise. - /// The output stream to write the line data into. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - internal bool ReadLine (Stream ostream, CancellationToken cancellationToken) - { - return ReadLineAsync (ostream, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously reads a single line of input from the stream. - /// - /// - /// This method should be called in a loop until it returns true. - /// - /// true, if reading the line is complete, false otherwise. - /// The output stream to write the line data into. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - internal Task ReadLineAsync (Stream ostream, CancellationToken cancellationToken) - { - return ReadLineAsync (ostream, true, cancellationToken); - } - - async Task WriteAsync (byte[] buffer, int offset, int count, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - try { - var network = NetworkStream.Get (Stream); - int index = offset; - int left = count; - - while (left > 0) { - int n = Math.Min (BlockSize - outputIndex, left); - - if (outputIndex > 0 || n < BlockSize) { - // append the data to the output buffer - Buffer.BlockCopy (buffer, index, output, outputIndex, n); - outputIndex += n; - index += n; - left -= n; - } - - if (outputIndex == BlockSize) { - // flush the output buffer - if (doAsync) { - await Stream.WriteAsync (output, 0, BlockSize, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (output, 0, BlockSize); - } - logger.LogClient (output, 0, BlockSize); - outputIndex = 0; - } - - if (outputIndex == 0) { - // write blocks of data to the stream without buffering - while (left >= BlockSize) { - if (doAsync) { - await Stream.WriteAsync (buffer, index, BlockSize, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (buffer, index, BlockSize); - } - logger.LogClient (buffer, index, BlockSize); - index += BlockSize; - left -= BlockSize; - } - } - } - } catch (Exception ex) { - IsConnected = false; - if (!(ex is OperationCanceledException)) - cancellationToken.ThrowIfCancellationRequested (); - throw; - } - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void Write (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - WriteAsync (buffer, offset, count, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - Write (buffer, offset, count, CancellationToken.None); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return WriteAsync (buffer, offset, count, true, cancellationToken); - } - - async Task FlushAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (outputIndex == 0) - return; - - try { - if (doAsync) { - await Stream.WriteAsync (output, 0, outputIndex, cancellationToken).ConfigureAwait (false); - await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - var network = NetworkStream.Get (Stream); - - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (output, 0, outputIndex); - Stream.Flush (); - } - logger.LogClient (output, 0, outputIndex); - outputIndex = 0; - } catch (Exception ex) { - IsConnected = false; - if (!(ex is OperationCanceledException)) - cancellationToken.ThrowIfCancellationRequested (); - throw; - } - } - - /// - /// Clears all output buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all output buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void Flush (CancellationToken cancellationToken) - { - FlushAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Clears all output buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all output buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Flush () - { - Flush (CancellationToken.None); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task FlushAsync (CancellationToken cancellationToken) - { - return FlushAsync (true, cancellationToken); - } - - /// - /// Sets the position within the current stream. - /// - /// - /// It is not possible to seek within a . - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// The stream does not support seeking. - /// - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - /// - /// Sets the length of the stream. - /// - /// - /// It is not possible to set the length of a . - /// - /// The desired length of the stream in bytes. - /// - /// The stream does not support setting the length. - /// - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - IsConnected = false; - Stream.Dispose (); - } - - disposed = true; - - base.Dispose (disposing); - } - } -} diff --git a/src/MailKit/Net/Imap/ImapToken.cs b/src/MailKit/Net/Imap/ImapToken.cs deleted file mode 100644 index d67e229..0000000 --- a/src/MailKit/Net/Imap/ImapToken.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -// ImapToken.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Globalization; - -namespace MailKit.Net.Imap { - enum ImapTokenType { - NoData = -7, - Error = -6, - Nil = -5, - Atom = -4, - Flag = -3, - QString = -2, - Literal = -1, - - // character tokens: - Eoln = (int) '\n', - OpenParen = (int) '(', - CloseParen = (int) ')', - Asterisk = (int) '*', - OpenBracket = (int) '[', - CloseBracket = (int) ']', - } - - class ImapToken - { - public readonly ImapTokenType Type; - public readonly object Value; - - public ImapToken (ImapTokenType type, object value = null) - { - Value = value; - Type = type; - - //System.Console.WriteLine ("token: {0}", this); - } - - public override string ToString () - { - switch (Type) { - case ImapTokenType.NoData: return ""; - case ImapTokenType.Nil: return "NIL"; - case ImapTokenType.Atom: return "[atom: " + (string) Value + "]"; - case ImapTokenType.Flag: return "[flag: " + (string) Value + "]"; - case ImapTokenType.QString: return "[qstring: \"" + (string) Value + "\"]"; - case ImapTokenType.Literal: return "{" + (int) Value + "}"; - case ImapTokenType.Eoln: return "'\\n'"; - case ImapTokenType.OpenParen: return "'('"; - case ImapTokenType.CloseParen: return "')'"; - case ImapTokenType.Asterisk: return "'*'"; - case ImapTokenType.OpenBracket: return "'['"; - case ImapTokenType.CloseBracket: return "']'"; - default: return string.Format (CultureInfo.InvariantCulture, "[{0}: '{1}']", Type, Value); - } - } - } -} diff --git a/src/MailKit/Net/Imap/ImapUtils.cs b/src/MailKit/Net/Imap/ImapUtils.cs deleted file mode 100644 index aec0733..0000000 --- a/src/MailKit/Net/Imap/ImapUtils.cs +++ /dev/null @@ -1,1670 +0,0 @@ -// -// ImapUtils.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Text; -using System.Threading; -using System.Diagnostics; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -using MimeKit; -using MimeKit.Utils; - -namespace MailKit.Net.Imap { - /// - /// IMAP utility functions. - /// - static class ImapUtils - { - const FolderAttributes SpecialUseAttributes = FolderAttributes.All | FolderAttributes.Archive | FolderAttributes.Drafts | - FolderAttributes.Flagged | FolderAttributes.Important | FolderAttributes.Inbox | FolderAttributes.Junk | - FolderAttributes.Sent | FolderAttributes.Trash; - const string QuotedSpecials = " \t()<>@,;:\\\"/[]?="; - static readonly int InboxLength = "INBOX".Length; - - static readonly string[] Months = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - - /// - /// Formats a date in a format suitable for use with the APPEND command. - /// - /// The formatted date string. - /// The date. - public static string FormatInternalDate (DateTimeOffset date) - { - return string.Format (CultureInfo.InvariantCulture, "{0:D2}-{1}-{2:D4} {3:D2}:{4:D2}:{5:D2} {6:+00;-00}{7:00}", - date.Day, Months[date.Month - 1], date.Year, date.Hour, date.Minute, date.Second, - date.Offset.Hours, date.Offset.Minutes); - } - - class UniqueHeaderSet : HashSet - { - public UniqueHeaderSet () : base (StringComparer.Ordinal) - { - } - } - - public static HashSet GetUniqueHeaders (IEnumerable headers) - { - if (headers == null) - throw new ArgumentNullException (nameof (headers)); - - // check if this list of headers is already unique (e.g. created by GetUniqueHeaders (IEnumerable)) - if (headers is UniqueHeaderSet unique) - return unique; - - var hash = new UniqueHeaderSet (); - - foreach (var header in headers) { - if (header.Length == 0) - throw new ArgumentException ($"Invalid header field: {header}", nameof (headers)); - - for (int i = 0; i < header.Length; i++) { - char c = header[i]; - - if (c <= 32 || c >= 127 || c == ':') - throw new ArgumentException ($"Illegal characters in header field: {header}", nameof (headers)); - } - - hash.Add (header.ToUpperInvariant ()); - } - - return hash; - } - - public static HashSet GetUniqueHeaders (IEnumerable headers) - { - if (headers == null) - throw new ArgumentNullException (nameof (headers)); - - var hash = new UniqueHeaderSet (); - - foreach (var header in headers) { - if (header == HeaderId.Unknown) - continue; - - hash.Add (header.ToHeaderName ().ToUpperInvariant ()); - } - - return hash; - } - - static bool TryGetInt32 (string text, ref int index, out int value) - { - int startIndex = index; - - value = 0; - - while (index < text.Length && text[index] >= '0' && text[index] <= '9') { - int digit = text[index] - '0'; - - if (value > int.MaxValue / 10 || (value == int.MaxValue / 10 && digit > int.MaxValue % 10)) { - // integer overflow - return false; - } - - value = (value * 10) + digit; - index++; - } - - return index > startIndex; - } - - static bool TryGetInt32 (string text, ref int index, char delim, out int value) - { - return TryGetInt32 (text, ref index, out value) && index < text.Length && text[index] == delim; - } - - static bool TryGetMonth (string text, ref int index, char delim, out int month) - { - int startIndex = index; - - month = 0; - - if ((index = text.IndexOf (delim, index)) == -1 || (index - startIndex) != 3) - return false; - - for (int i = 0; i < Months.Length; i++) { - if (string.Compare (Months[i], 0, text, startIndex, 3, StringComparison.OrdinalIgnoreCase) == 0) { - month = i + 1; - return true; - } - } - - return false; - } - - static bool TryGetTimeZone (string text, ref int index, out TimeSpan timezone) - { - int tzone, sign = 1; - - if (text[index] == '-') { - sign = -1; - index++; - } else if (text[index] == '+') { - index++; - } - - if (!TryGetInt32 (text, ref index, out tzone)) { - timezone = new TimeSpan (); - return false; - } - - tzone *= sign; - - while (tzone < -1400) - tzone += 2400; - - while (tzone > 1400) - tzone -= 2400; - - int minutes = tzone % 100; - int hours = tzone / 100; - - timezone = new TimeSpan (hours, minutes, 0); - - return true; - } - - static Exception InvalidInternalDateFormat (string text) - { - return new FormatException ("Invalid INTERNALDATE format: " + text); - } - - /// - /// Parses the internal date string. - /// - /// The date. - /// The text to parse. - public static DateTimeOffset ParseInternalDate (string text) - { - int day, month, year, hour, minute, second; - TimeSpan timezone; - int index = 0; - - while (index < text.Length && char.IsWhiteSpace (text[index])) - index++; - - if (index >= text.Length || !TryGetInt32 (text, ref index, '-', out day) || day < 1 || day > 31) - throw InvalidInternalDateFormat (text); - - index++; - if (index >= text.Length || !TryGetMonth (text, ref index, '-', out month)) - throw InvalidInternalDateFormat (text); - - index++; - if (index >= text.Length || !TryGetInt32 (text, ref index, ' ', out year) || year < 1969) - throw InvalidInternalDateFormat (text); - - index++; - if (index >= text.Length || !TryGetInt32 (text, ref index, ':', out hour) || hour > 23) - throw InvalidInternalDateFormat (text); - - index++; - if (index >= text.Length || !TryGetInt32 (text, ref index, ':', out minute) || minute > 59) - throw InvalidInternalDateFormat (text); - - index++; - if (index >= text.Length || !TryGetInt32 (text, ref index, ' ', out second) || second > 59) - throw InvalidInternalDateFormat (text); - - index++; - if (index >= text.Length || !TryGetTimeZone (text, ref index, out timezone)) - throw InvalidInternalDateFormat (text); - - while (index < text.Length && char.IsWhiteSpace (text[index])) - index++; - - if (index < text.Length) - throw InvalidInternalDateFormat (text); - - // return DateTimeOffset.ParseExact (text.Trim (), "d-MMM-yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture.DateTimeFormat); - return new DateTimeOffset (year, month, day, hour, minute, second, timezone); - } - - /// - /// Formats a list of annotations for a STORE or APPEND command. - /// - /// The command builder. - /// The annotations. - /// the argument list. - /// Throw an exception if there are any annotations without properties. - public static void FormatAnnotations (StringBuilder command, IList annotations, List args, bool throwOnError) - { - int length = command.Length; - int added = 0; - - command.Append ("ANNOTATION ("); - - for (int i = 0; i < annotations.Count; i++) { - var annotation = annotations[i]; - - if (annotation.Properties.Count == 0) { - if (throwOnError) - throw new ArgumentException ("One or more annotations does not define any attributes.", nameof (annotations)); - - continue; - } - - command.Append (annotation.Entry); - command.Append (" ("); - - foreach (var property in annotation.Properties) { - command.AppendFormat ("{0} %S ", property.Key); - args.Add (property.Value); - } - - command[command.Length - 1] = ')'; - command.Append (' '); - - added++; - } - - if (added > 0) - command[command.Length - 1] = ')'; - else - command.Length = length; - } - - /// - /// Formats the array of indexes as a string suitable for use with IMAP commands. - /// - /// The index set. - /// The indexes. - /// - /// is null. - /// - /// - /// One or more of the indexes has a negative value. - /// - public static string FormatIndexSet (IList indexes) - { - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (indexes.Count == 0) - throw new ArgumentException ("No indexes were specified.", nameof (indexes)); - - var builder = new StringBuilder (); - int index = 0; - - while (index < indexes.Count) { - if (indexes[index] < 0) - throw new ArgumentException ("One or more of the indexes is negative.", nameof (indexes)); - - int begin = indexes[index]; - int end = indexes[index]; - int i = index + 1; - - if (i < indexes.Count) { - if (indexes[i] == end + 1) { - end = indexes[i++]; - - while (i < indexes.Count && indexes[i] == end + 1) { - end++; - i++; - } - } else if (indexes[i] == end - 1) { - end = indexes[i++]; - - while (i < indexes.Count && indexes[i] == end - 1) { - end--; - i++; - } - } - } - - if (builder.Length > 0) - builder.Append (','); - - if (begin != end) - builder.AppendFormat (CultureInfo.InvariantCulture, "{0}:{1}", begin + 1, end + 1); - else - builder.Append ((begin + 1).ToString (CultureInfo.InvariantCulture)); - - index = i; - } - - return builder.ToString (); - } - - /// - /// Parses an untagged ID response. - /// - /// The IMAP engine. - /// The IMAP command. - /// The index. - /// Whether or not asynchronous IO methods should be used. - public static async Task ParseImplementationAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ID", "{0}"); - var token = await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - ImapImplementation implementation; - - if (token.Type == ImapTokenType.Nil) - return; - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - - implementation = new ImapImplementation (); - - while (token.Type != ImapTokenType.CloseParen) { - var property = await ReadStringTokenAsync (engine, format, doAsync, ic.CancellationToken).ConfigureAwait (false); - var value = await ReadNStringTokenAsync (engine, format, false, doAsync, ic.CancellationToken).ConfigureAwait (false); - - implementation.Properties[property] = value; - - token = await engine.PeekTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } - - ic.UserData = implementation; - - // read the ')' token - await engine.ReadTokenAsync (doAsync, ic.CancellationToken).ConfigureAwait (false); - } - - /// - /// Canonicalize the name of the mailbox. - /// - /// - /// Canonicalizes the name of the mailbox by replacing various - /// capitalizations of "INBOX" with the literal "INBOX" string. - /// - /// The mailbox name. - /// The encoded mailbox name. - /// The directory separator. - public static string CanonicalizeMailboxName (string mailboxName, char directorySeparator) - { - if (!mailboxName.StartsWith ("INBOX", StringComparison.OrdinalIgnoreCase)) - return mailboxName; - - if (mailboxName.Length > InboxLength && mailboxName[InboxLength] == directorySeparator) - return "INBOX" + mailboxName.Substring (InboxLength); - - if (mailboxName.Length == InboxLength) - return "INBOX"; - - return mailboxName; - } - - /// - /// Determines whether the specified mailbox is the Inbox. - /// - /// true if the specified mailbox name is the Inbox; otherwise, false. - /// The mailbox name. - public static bool IsInbox (string mailboxName) - { - return string.Compare (mailboxName, "INBOX", StringComparison.OrdinalIgnoreCase) == 0; - } - - static async Task ReadFolderNameAsync (ImapEngine engine, char delim, string format, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - string encodedName; - - switch (token.Type) { - case ImapTokenType.Literal: - encodedName = await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - encodedName = (string) token.Value; - - // Note: Exchange apparently doesn't quote folder names that contain tabs. - // - // See https://github.com/jstedfast/MailKit/issues/945 for details. - if (engine.QuirksMode == ImapQuirksMode.Exchange) { - var line = await engine.ReadLineAsync (doAsync, cancellationToken); - int eoln = line.IndexOf ("\r\n", StringComparison.Ordinal); - eoln = eoln != -1 ? eoln : line.Length - 1; - - // unget the \r\n sequence - token = new ImapToken (ImapTokenType.Eoln); - engine.Stream.UngetToken (token); - - if (eoln > 0) - encodedName += line.Substring (0, eoln); - } - break; - case ImapTokenType.Nil: - // Note: according to rfc3501, section 4.5, NIL is acceptable as a mailbox name. - return "NIL"; - default: - throw ImapEngine.UnexpectedToken (format, token); - } - - return encodedName.TrimEnd (delim); - } - - /// - /// Parses an untagged LIST or LSUB response. - /// - /// The IMAP engine. - /// The list of folders to be populated. - /// true if it is an LSUB response; otherwise, false. - /// true if the LIST response is expected to return \Subscribed flags; otherwise, false. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public static async Task ParseFolderListAsync (ImapEngine engine, List list, bool isLsub, bool returnsSubscribed, bool doAsync, CancellationToken cancellationToken) - { - var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, isLsub ? "LSUB" : "LIST", "{0}"); - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - var attrs = FolderAttributes.None; - ImapFolder folder = null; - string encodedName; - char delim; - - // parse the folder attributes list - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - while (token.Type == ImapTokenType.Flag || token.Type == ImapTokenType.Atom) { - var atom = (string) token.Value; - - switch (atom) { - case "\\NoInferiors": attrs |= FolderAttributes.NoInferiors; break; - case "\\Noselect": attrs |= FolderAttributes.NoSelect; break; - case "\\Marked": attrs |= FolderAttributes.Marked; break; - case "\\Unmarked": attrs |= FolderAttributes.Unmarked; break; - case "\\NonExistent": attrs |= FolderAttributes.NonExistent; break; - case "\\Subscribed": attrs |= FolderAttributes.Subscribed; break; - case "\\Remote": attrs |= FolderAttributes.Remote; break; - case "\\HasChildren": attrs |= FolderAttributes.HasChildren; break; - case "\\HasNoChildren": attrs |= FolderAttributes.HasNoChildren; break; - case "\\All": attrs |= FolderAttributes.All; break; - case "\\Archive": attrs |= FolderAttributes.Archive; break; - case "\\Drafts": attrs |= FolderAttributes.Drafts; break; - case "\\Flagged": attrs |= FolderAttributes.Flagged; break; - case "\\Important": attrs |= FolderAttributes.Important; break; - case "\\Junk": attrs |= FolderAttributes.Junk; break; - case "\\Sent": attrs |= FolderAttributes.Sent; break; - case "\\Trash": attrs |= FolderAttributes.Trash; break; - // XLIST flags: - case "\\AllMail": attrs |= FolderAttributes.All; break; - case "\\Inbox": attrs |= FolderAttributes.Inbox; break; - case "\\Spam": attrs |= FolderAttributes.Junk; break; - case "\\Starred": attrs |= FolderAttributes.Flagged; break; - } - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, format, token); - - // parse the path delimeter - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.QString) { - var qstring = (string) token.Value; - - delim = qstring[0]; - } else if (token.Type == ImapTokenType.Nil) { - delim = '\0'; - } else { - throw ImapEngine.UnexpectedToken (format, token); - } - - encodedName = await ReadFolderNameAsync (engine, delim, format, doAsync, cancellationToken).ConfigureAwait (false); - - if (IsInbox (encodedName)) - attrs |= FolderAttributes.Inbox; - - // peek at the next token to see if we have a LIST extension - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenParen) { - var renamed = false; - - // read the '(' token - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - // a LIST extension - - ImapEngine.AssertToken (token, ImapTokenType.Atom, ImapTokenType.QString, format, token); - - var atom = (string) token.Value; - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - engine.Stream.UngetToken (token); - - if (!renamed && atom.Equals ("OLDNAME", StringComparison.OrdinalIgnoreCase)) { - var oldEncodedName = await ReadFolderNameAsync (engine, delim, format, doAsync, cancellationToken).ConfigureAwait (false); - - if (engine.FolderCache.TryGetValue (oldEncodedName, out ImapFolder oldFolder)) { - var args = new ImapFolderConstructorArgs (engine, encodedName, attrs, delim); - - engine.FolderCache.Remove (oldEncodedName); - engine.FolderCache[encodedName] = oldFolder; - oldFolder.OnRenamed (args); - folder = oldFolder; - } - - renamed = true; - } else { - await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false); - } - } while (true); - } while (true); - } else { - ImapEngine.AssertToken (token, ImapTokenType.Eoln, format, token); - } - - if (folder != null || engine.GetCachedFolder (encodedName, out folder)) { - if ((attrs & FolderAttributes.NonExistent) != 0) { - folder.UpdatePermanentFlags (MessageFlags.None); - folder.UpdateAcceptedFlags (MessageFlags.None); - folder.UpdateUidNext (UniqueId.Invalid); - folder.UpdateHighestModSeq (0); - folder.UpdateUidValidity (0); - folder.UpdateUnread (0); - } - - if (isLsub) { - // Note: merge all pre-existing attributes since the LSUB response will not contain them - attrs |= folder.Attributes | FolderAttributes.Subscribed; - } else { - // Note: only merge the SPECIAL-USE and \Subscribed attributes for a LIST command - attrs |= folder.Attributes & SpecialUseAttributes; - - // Note: only merge \Subscribed if the LIST command isn't expected to include it - if (!returnsSubscribed) - attrs |= folder.Attributes & FolderAttributes.Subscribed; - } - - folder.UpdateAttributes (attrs); - } else { - folder = engine.CreateImapFolder (encodedName, attrs, delim); - engine.CacheFolder (folder); - - if (list == null) - engine.OnFolderCreated (folder); - } - - // Note: list will be null if this is an unsolicited LIST response due to an active NOTIFY request - list?.Add (folder); - } - - /// - /// Parses an untagged LIST or LSUB response. - /// - /// The IMAP engine. - /// The IMAP command. - /// The index. - /// Whether or not asynchronous IO methods should be used. - public static Task ParseFolderListAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var list = (List) ic.UserData; - - return ParseFolderListAsync (engine, list, ic.Lsub, ic.ListReturnsSubscribed, doAsync, ic.CancellationToken); - } - - /// - /// Parses an untagged METADATA response. - /// - /// The encoded name of the folder that the metadata belongs to. - /// The IMAP engine. - /// The metadata collection to be populated. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public static async Task ParseMetadataAsync (ImapEngine engine, MetadataCollection metadata, bool doAsync, CancellationToken cancellationToken) - { - var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "METADATA", "{0}"); - var encodedName = await ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - while (token.Type != ImapTokenType.CloseParen) { - var tag = await ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - var value = await ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - metadata.Add (new Metadata (MetadataTag.Create (tag), value) { EncodedName = encodedName }); - - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - // read the closing paren - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - /// - /// Parses an untagged METADATA response. - /// - /// The IMAP engine. - /// The IMAP command. - /// The index. - /// Whether or not asynchronous IO methods should be used. - public static Task ParseMetadataAsync (ImapEngine engine, ImapCommand ic, int index, bool doAsync) - { - var metadata = (MetadataCollection) ic.UserData; - - return ParseMetadataAsync (engine, metadata, doAsync, ic.CancellationToken); - } - - internal static async Task ReadStringTokenAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - switch (token.Type) { - case ImapTokenType.Literal: - return await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - case ImapTokenType.QString: - case ImapTokenType.Atom: - return (string) token.Value; - default: - throw ImapEngine.UnexpectedToken (format, token); - } - } - - static async Task ReadNStringTokenAsync (ImapEngine engine, string format, bool rfc2047, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - string value; - - switch (token.Type) { - case ImapTokenType.Literal: - value = await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - value = (string) token.Value; - break; - case ImapTokenType.Nil: - return null; - default: - throw ImapEngine.UnexpectedToken (format, token); - } - - if (rfc2047) { - var encoding = engine.UTF8Enabled ? ImapEngine.UTF8 : ImapEngine.Latin1; - - return Rfc2047.DecodeText (encoding.GetBytes (value)); - } - - return value; - } - - static async Task ReadNumberAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - // Note: this is a work-around for broken IMAP servers that return negative integer values for things - // like octet counts and line counts. - if (token.Type == ImapTokenType.Atom) { - var atom = (string) token.Value; - - if (atom.Length > 0 && atom[0] == '-') { - if (!int.TryParse (atom, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var negative)) - throw ImapEngine.UnexpectedToken (format, token); - - // Note: since Octets & Lines are the only 2 values this method is responsible for parsing, - // it seems the only sane value to return would be 0. - return 0; - } - } - - return ImapEngine.ParseNumber (token, false, format, token); - } - - static bool NeedsQuoting (string value) - { - for (int i = 0; i < value.Length; i++) { - if (value[i] > 127 || char.IsControl (value[i])) - return true; - - if (QuotedSpecials.IndexOf (value[i]) != -1) - return true; - } - - return value.Length == 0; - } - - static async Task ParseParameterListAsync (StringBuilder builder, ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - ImapToken token; - - do { - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - var name = await ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - // Note: technically, the value should also be a 'string' token and not an 'nstring', - // but issue #124 reveals a server that is sending NIL for boundary values. - var value = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false) ?? string.Empty; - - builder.Append ("; ").Append (name).Append ('='); - - if (NeedsQuoting (value)) - builder.Append (MimeUtils.Quote (value)); - else - builder.Append (value); - } while (true); - - // read the ')' - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - static async Task ParseContentTypeAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var type = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false) ?? "application"; - var token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - ContentType contentType; - string subtype; - - if (token.Type == ImapTokenType.OpenParen || token.Type == ImapTokenType.Nil) { - // Note: work around broken IMAP server implementations... - if (engine.QuirksMode == ImapQuirksMode.GMail) { - // Note: GMail's IMAP server implementation breaks when it encounters - // nested multiparts with the same boundary and returns a BODYSTRUCTURE - // like the example in https://github.com/jstedfast/MailKit/issues/205 or - // like the example in https://github.com/jstedfast/MailKit/issues/777 - // - // Note: this token is either '(' to start the Content-Type parameter values - // or it is a NIL to specify that there are no parameter values. - return type; - } - - if (token.Type != ImapTokenType.Nil) { - // Note: In other IMAP server implementations, such as the one found in - // https://github.com/jstedfast/MailKit/issues/371, if the server comes - // across something like "Content-Type: X-ZIP", it will only send a - // media-subtype token and completely fail to send a media-type token. - subtype = type; - type = "application"; - } else { - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - subtype = string.Empty; - } - } else { - subtype = await ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - } - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Nil) - return new ContentType (type, subtype); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - var builder = new StringBuilder (); - builder.AppendFormat ("{0}/{1}", type, subtype); - - await ParseParameterListAsync (builder, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - if (!ContentType.TryParse (builder.ToString (), out contentType)) - contentType = new ContentType (type, subtype); - - return contentType; - } - - static async Task ParseContentDispositionAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - // body-fld-dsp = "(" string SP body-fld-param ")" / nil - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Nil) - return null; - - if (token.Type != ImapTokenType.OpenParen) { - // Note: this is a work-around for issue #919 where Exchange sends `"inline"` instead of `("inline" NIL)` - if (token.Type == ImapTokenType.Atom || token.Type == ImapTokenType.QString) - return new ContentDisposition ((string) token.Value); - - throw ImapEngine.UnexpectedToken (format, token); - } - - // Exchange bug: ... (NIL NIL) ... - var dsp = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false); - var builder = new StringBuilder (); - ContentDisposition disposition; - bool isNil = false; - - // Note: These are work-arounds for some bugs in some mail clients that - // either leave out the disposition value or quote it. - // - // See https://github.com/jstedfast/MailKit/issues/486 for details. - if (string.IsNullOrEmpty (dsp)) - builder.Append (ContentDisposition.Attachment); - else - builder.Append (dsp.Trim ('"')); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenParen) - await ParseParameterListAsync (builder, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - else if (token.Type != ImapTokenType.Nil) - throw ImapEngine.UnexpectedToken (format, token); - else - isNil = true; - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, format, token); - - if (dsp == null && isNil) - return null; - - ContentDisposition.TryParse (builder.ToString (), out disposition); - - return disposition; - } - - static async Task ParseContentLanguageAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - var languages = new List (); - string language; - - switch (token.Type) { - case ImapTokenType.Literal: - language = await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - languages.Add (language); - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - language = (string) token.Value; - languages.Add (language); - break; - case ImapTokenType.Nil: - return null; - case ImapTokenType.OpenParen: - do { - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - // Note: Some broken IMAP servers send `NIL` tokens in this list. Just ignore them. - // - // See https://github.com/jstedfast/MailKit/issues/953 - language = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false); - - if (language != null) - languages.Add (language); - } while (true); - - // read the ')' - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - default: - throw ImapEngine.UnexpectedToken (format, token); - } - - return languages.ToArray (); - } - - static async Task ParseContentLocationAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var location = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false); - - if (string.IsNullOrWhiteSpace (location)) - return null; - - if (Uri.IsWellFormedUriString (location, UriKind.Absolute)) - return new Uri (location, UriKind.Absolute); - - if (Uri.IsWellFormedUriString (location, UriKind.Relative)) - return new Uri (location, UriKind.Relative); - - return null; - } - - static async Task SkipBodyExtensionAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - switch (token.Type) { - case ImapTokenType.OpenParen: - do { - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - await SkipBodyExtensionAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - } while (true); - - // read the ')' - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapTokenType.Literal: - await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - case ImapTokenType.Nil: - break; - default: - throw ImapEngine.UnexpectedToken (format, token); - } - } - - static async Task ParseMultipartAsync (ImapEngine engine, string format, string path, string subtype, bool doAsync, CancellationToken cancellationToken) - { - var prefix = path.Length > 0 ? path + "." : string.Empty; - var body = new BodyPartMultipart (); - ImapToken token; - int index = 1; - - // Note: if subtype is not null, then we are working around a GMail bug... - if (subtype == null) { - do { - body.BodyParts.Add (await ParseBodyAsync (engine, format, prefix + index, doAsync, cancellationToken).ConfigureAwait (false)); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - index++; - } while (token.Type == ImapTokenType.OpenParen); - - subtype = await ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - } - - body.ContentType = new ContentType ("multipart", subtype); - body.PartSpecifier = path; - - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type != ImapTokenType.CloseParen) { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapTokenType.Nil, format, token); - - var builder = new StringBuilder (); - ContentType contentType; - - builder.AppendFormat ("{0}/{1}", body.ContentType.MediaType, body.ContentType.MediaSubtype); - - if (token.Type == ImapTokenType.OpenParen) - await ParseParameterListAsync (builder, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - if (ContentType.TryParse (builder.ToString (), out contentType)) - body.ContentType = contentType; - - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - if (token.Type == ImapTokenType.QString) { - // Note: This is a work-around for broken Exchange servers. - // - // See https://stackoverflow.com/questions/33481604/mailkit-fetch-unexpected-token-in-imap-response-qstring-multipart-message - // for details. - - // Read what appears to be a Content-Description. - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - // Peek ahead at the next token. It has been suggested that this next token seems to be the Content-Language value. - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } else if (token.Type != ImapTokenType.CloseParen) { - body.ContentDisposition = await ParseContentDispositionAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - if (token.Type != ImapTokenType.CloseParen) { - body.ContentLanguage = await ParseContentLanguageAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - if (token.Type != ImapTokenType.CloseParen) { - body.ContentLocation = await ParseContentLocationAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - while (token.Type != ImapTokenType.CloseParen) { - await SkipBodyExtensionAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - // read the ')' - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - return body; - } - - public static async Task ParseBodyAsync (ImapEngine engine, string format, string path, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Nil) - return null; - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - // Note: If we immediately get a closing ')', then treat it the same as if we had gotten a `NIL` `body` token. - // - // See https://github.com/jstedfast/MailKit/issues/944 for details. - if (token.Type == ImapTokenType.CloseParen) { - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - return null; - } - - if (token.Type == ImapTokenType.OpenParen) - return await ParseMultipartAsync (engine, format, path, null, doAsync, cancellationToken).ConfigureAwait (false); - - var result = await ParseContentTypeAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - if (result is string) { - // GMail breakage... yay! What we have is a nested multipart with - // the same boundary as its parent. - return await ParseMultipartAsync (engine, format, path, (string) result, doAsync, cancellationToken).ConfigureAwait (false); - } - - var id = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false); - var desc = await ReadNStringTokenAsync (engine, format, true, doAsync, cancellationToken).ConfigureAwait (false); - // Note: technically, body-fld-enc, is not allowed to be NIL, but we need to deal with broken servers... - var enc = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false); - var octets = await ReadNumberAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - var type = (ContentType) result; - var isMultipart = false; - BodyPartBasic body; - - if (type.IsMimeType ("message", "rfc822")) { - var mesg = new BodyPartMessage (); - - // Note: GMail (and potentially other IMAP servers) will send body-part-basic - // expressions instead of body-part-msg expressions when they encounter - // message/rfc822 MIME parts that are illegally encoded using base64 (or - // quoted-printable?). According to rfc3501, IMAP servers are REQUIRED to - // send body-part-msg expressions for message/rfc822 parts, however, it is - // understandable why GMail (and other IMAP servers?) do what they do in this - // particular case. - // - // For examples, see issue #32 and issue #59. - // - // The workaround is to check for the expected '(' signifying an envelope token. - // If we do not get an '(', then we are likely looking at the Content-MD5 token - // which gets handled below. - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.OpenParen) { - mesg.Envelope = await ParseEnvelopeAsync (engine, doAsync, cancellationToken).ConfigureAwait (false); - mesg.Body = await ParseBodyAsync (engine, format, path, doAsync, cancellationToken).ConfigureAwait (false); - mesg.Lines = await ReadNumberAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - } - - body = mesg; - } else if (type.IsMimeType ("text", "*")) { - var text = new BodyPartText (); - text.Lines = await ReadNumberAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - body = text; - } else { - isMultipart = type.IsMimeType ("multipart", "*"); - body = new BodyPartBasic (); - } - - body.ContentTransferEncoding = enc; - body.ContentDescription = desc; - body.PartSpecifier = path; - body.ContentType = type; - body.ContentId = id; - body.Octets = octets; - - // if we are parsing a BODYSTRUCTURE, we may get some more tokens before the ')' - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (!isMultipart) { - if (token.Type != ImapTokenType.CloseParen) { - body.ContentMd5 = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - if (token.Type != ImapTokenType.CloseParen) { - body.ContentDisposition = await ParseContentDispositionAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - if (token.Type != ImapTokenType.CloseParen) { - body.ContentLanguage = await ParseContentLanguageAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - if (token.Type != ImapTokenType.CloseParen) { - body.ContentLocation = await ParseContentLocationAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - } - - while (token.Type != ImapTokenType.CloseParen) { - await SkipBodyExtensionAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - // read the ')' - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - return body; - } - - struct EnvelopeAddress - { - public readonly string Name; - public readonly string Route; - public readonly string Mailbox; - public readonly string Domain; - - public EnvelopeAddress (string[] values) - { - Name = values[0]; - Route = values[1]; - Mailbox = values[2]; - Domain = values[3]; - } - - public bool IsGroupStart { - get { return Name == null && Route == null && Mailbox != null && Domain == null; } - } - - public bool IsGroupEnd { - get { return Name == null && Route == null && Mailbox == null && Domain == null; } - } - - public MailboxAddress ToMailboxAddress (ImapEngine engine) - { - var mailbox = Mailbox; - var domain = Domain; - string name = null; - - if (Name != null) { - var encoding = engine.UTF8Enabled ? ImapEngine.UTF8 : ImapEngine.Latin1; - - name = Rfc2047.DecodePhrase (encoding.GetBytes (Name)); - } - - // Note: When parsing mailbox addresses w/o a domain, Dovecot will - // use "MISSING_DOMAIN" as the domain string to prevent it from - // appearing as a group address in the IMAP ENVELOPE response. - if (domain == "MISSING_DOMAIN" || domain == ".MISSING-HOST-NAME.") - domain = null; - else if (domain != null) - domain = domain.TrimEnd ('>'); - - if (mailbox != null) - mailbox = mailbox.TrimStart ('<'); - - string address = domain != null ? mailbox + "@" + domain : mailbox; - DomainList route; - - if (Route != null && DomainList.TryParse (Route, out route)) - return new MailboxAddress (name, route, address); - - return new MailboxAddress (name, address); - } - - public GroupAddress ToGroupAddress (ImapEngine engine) - { - var name = string.Empty; - - if (Mailbox != null) { - var encoding = engine.UTF8Enabled ? ImapEngine.UTF8 : ImapEngine.Latin1; - - name = Rfc2047.DecodePhrase (encoding.GetBytes (Mailbox)); - } - - return new GroupAddress (name); - } - } - - static async Task ParseEnvelopeAddressAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var values = new string[4]; - ImapToken token; - int index = 0; - - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - switch (token.Type) { - case ImapTokenType.Literal: - values[index] = await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - values[index] = (string) token.Value; - break; - case ImapTokenType.Nil: - break; - default: - throw ImapEngine.UnexpectedToken (format, token); - } - - index++; - } while (index < 4); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, format, token); - - return new EnvelopeAddress (values); - } - - static async Task ParseEnvelopeAddressListAsync (InternetAddressList list, ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Nil) - return; - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - var stack = new List (); - int sp = 0; - - stack.Add (list); - - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - // Note: As seen in https://github.com/jstedfast/MailKit/issues/991, it seems that SmarterMail IMAP - // servers will sometimes include a NIL address token within the address list. Just ignore it. - if (token.Type == ImapTokenType.Nil) - continue; - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - var address = await ParseEnvelopeAddressAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - if (address.IsGroupStart && engine.QuirksMode != ImapQuirksMode.GMail) { - var group = address.ToGroupAddress (engine); - stack[sp].Add (group); - stack.Add (group.Members); - sp++; - } else if (address.IsGroupEnd) { - if (sp > 0) { - stack.RemoveAt (sp); - sp--; - } - } else { - try { - // Note: We need to do a try/catch around ToMailboxAddress() because some addresses - // returned by the IMAP server might be completely horked. For an example, see the - // second error report in https://github.com/jstedfast/MailKit/issues/494 where one - // of the addresses in the ENVELOPE has the name and address tokens flipped. - var mailbox = address.ToMailboxAddress (engine); - stack[sp].Add (mailbox); - } catch { - continue; - } - } - } while (true); - } - - static async Task ParseEnvelopeDateAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - DateTimeOffset date; - string value; - - switch (token.Type) { - case ImapTokenType.Literal: - value = await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); - break; - case ImapTokenType.QString: - case ImapTokenType.Atom: - value = (string) token.Value; - break; - case ImapTokenType.Nil: - return null; - default: - throw ImapEngine.UnexpectedToken (format, token); - } - - if (!DateUtils.TryParse (value, out date)) - return null; - - return date; - } - - /// - /// Parses the ENVELOPE parenthesized list. - /// - /// The envelope. - /// The IMAP engine. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public static async Task ParseEnvelopeAsync (ImapEngine engine, bool doAsync, CancellationToken cancellationToken) - { - string format = string.Format (ImapEngine.GenericItemSyntaxErrorFormat, "ENVELOPE", "{0}"); - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - string nstring; - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, format, token); - - var envelope = new Envelope (); - envelope.Date = await ParseEnvelopeDateAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - envelope.Subject = await ReadNStringTokenAsync (engine, format, true, doAsync, cancellationToken).ConfigureAwait (false); - await ParseEnvelopeAddressListAsync (envelope.From, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - await ParseEnvelopeAddressListAsync (envelope.Sender, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - await ParseEnvelopeAddressListAsync (envelope.ReplyTo, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - await ParseEnvelopeAddressListAsync (envelope.To, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - await ParseEnvelopeAddressListAsync (envelope.Cc, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - await ParseEnvelopeAddressListAsync (envelope.Bcc, engine, format, doAsync, cancellationToken).ConfigureAwait (false); - - // Note: Some broken IMAP servers will forget to include the In-Reply-To token (I guess if the header isn't set?). - // - // See https://github.com/jstedfast/MailKit/issues/932 - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - if (token.Type != ImapTokenType.CloseParen) { - if ((nstring = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false)) != null) - envelope.InReplyTo = MimeUtils.EnumerateReferences (nstring).FirstOrDefault (); - - // Note: Some broken IMAP servers will forget to include the Message-Id token (I guess if the header isn't set?). - // - // See https://github.com/jstedfast/MailKit/issues/669 - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - if (token.Type != ImapTokenType.CloseParen) { - if ((nstring = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false)) != null) { - try { - envelope.MessageId = MimeUtils.ParseMessageId (nstring); - } catch { - envelope.MessageId = nstring; - } - } - } - } - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, format, token); - - return envelope; - } - - /// - /// Formats a flags list suitable for use with the APPEND command. - /// - /// The flags list string. - /// The message flags. - /// The number of keywords. - public static string FormatFlagsList (MessageFlags flags, int numKeywords) - { - var builder = new StringBuilder (); - - builder.Append ('('); - - if ((flags & MessageFlags.Answered) != 0) - builder.Append ("\\Answered "); - if ((flags & MessageFlags.Deleted) != 0) - builder.Append ("\\Deleted "); - if ((flags & MessageFlags.Draft) != 0) - builder.Append ("\\Draft "); - if ((flags & MessageFlags.Flagged) != 0) - builder.Append ("\\Flagged "); - if ((flags & MessageFlags.Seen) != 0) - builder.Append ("\\Seen "); - - for (int i = 0; i < numKeywords; i++) - builder.Append ("%S "); - - if (builder.Length > 1) - builder.Length--; - - builder.Append (')'); - - return builder.ToString (); - } - - /// - /// Parses the flags list. - /// - /// The message flags. - /// The IMAP engine. - /// The name of the flags being parsed. - /// A hash set of user-defined message flags that will be populated if non-null. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public static async Task ParseFlagsListAsync (ImapEngine engine, string name, HashSet keywords, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - var flags = MessageFlags.None; - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericItemSyntaxErrorFormat, name, token); - - token = await engine.ReadTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - - while (token.Type == ImapTokenType.Atom || token.Type == ImapTokenType.Flag || token.Type == ImapTokenType.QString || token.Type == ImapTokenType.Nil) { - if (token.Type != ImapTokenType.Nil) { - var flag = (string) token.Value; - - switch (flag) { - case "\\Answered": flags |= MessageFlags.Answered; break; - case "\\Deleted": flags |= MessageFlags.Deleted; break; - case "\\Draft": flags |= MessageFlags.Draft; break; - case "\\Flagged": flags |= MessageFlags.Flagged; break; - case "\\Seen": flags |= MessageFlags.Seen; break; - case "\\Recent": flags |= MessageFlags.Recent; break; - case "\\*": flags |= MessageFlags.UserDefined; break; - default: - if (keywords != null) - keywords.Add (flag); - break; - } - } - - token = await engine.ReadTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - } - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericItemSyntaxErrorFormat, name, token); - - return flags; - } - - /// - /// Parses the ANNOTATION list. - /// - /// The list of annotations. - /// The IMAP engine. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public static async Task> ParseAnnotationsAsync (ImapEngine engine, bool doAsync, CancellationToken cancellationToken) - { - var format = string.Format (ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "ANNOTATION", "{0}"); - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - var annotations = new List (); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericItemSyntaxErrorFormat, "ANNOTATION", token); - - do { - token = await engine.PeekTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - var path = await ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - var entry = AnnotationEntry.Parse (path); - var annotation = new Annotation (entry); - - annotations.Add (annotation); - - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - // Note: Unsolicited FETCH responses that include ANNOTATION data do not include attribute values. - if (token.Type == ImapTokenType.OpenParen) { - // consume the '(' - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - // read the attribute/value pairs - do { - token = await engine.PeekTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - var name = await ReadStringTokenAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false); - var value = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false); - var attribute = new AnnotationAttribute (name); - - annotation.Properties[attribute] = value; - } while (true); - - // consume the ')' - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - } while (true); - - // consume the ')' - await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - return new ReadOnlyCollection (annotations); - } - - /// - /// Parses the X-GM-LABELS list. - /// - /// The message labels. - /// The IMAP engine. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public static async Task> ParseLabelsListAsync (ImapEngine engine, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - var labels = new List (); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericItemSyntaxErrorFormat, "X-GM-LABELS", token); - - token = await engine.ReadTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - - while (token.Type == ImapTokenType.Flag || token.Type == ImapTokenType.Atom || token.Type == ImapTokenType.QString || token.Type == ImapTokenType.Nil) { - // Apparently it's possible to set a NIL label in GMail... - // - // See https://github.com/jstedfast/MailKit/issues/244 for an example. - if (token.Type != ImapTokenType.Nil) { - var label = engine.DecodeMailboxName ((string) token.Value); - - labels.Add (label); - } else { - labels.Add ("NIL"); - } - - token = await engine.ReadTokenAsync (ImapStream.AtomSpecials, doAsync, cancellationToken).ConfigureAwait (false); - } - - ImapEngine.AssertToken (token, ImapTokenType.CloseParen, ImapEngine.GenericItemSyntaxErrorFormat, "X-GM-LABELS", token); - - return new ReadOnlyCollection (labels); - } - - static async Task ParseThreadAsync (ImapEngine engine, uint uidValidity, bool doAsync, CancellationToken cancellationToken) - { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - MessageThread thread, node, child; - uint uid; - - if (token.Type == ImapTokenType.OpenParen) { - thread = new MessageThread ((UniqueId?) null /*UniqueId.Invalid*/); - - do { - child = await ParseThreadAsync (engine, uidValidity, doAsync, cancellationToken).ConfigureAwait (false); - thread.Children.Add (child); - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - } while (token.Type != ImapTokenType.CloseParen); - - return thread; - } - - uid = ImapEngine.ParseNumber (token, true, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "THREAD", token); - node = thread = new MessageThread (new UniqueId (uidValidity, uid)); - - do { - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.CloseParen) - break; - - if (token.Type == ImapTokenType.OpenParen) { - child = await ParseThreadAsync (engine, uidValidity, doAsync, cancellationToken).ConfigureAwait (false); - node.Children.Add (child); - } else { - uid = ImapEngine.ParseNumber (token, true, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "THREAD", token); - child = new MessageThread (new UniqueId (uidValidity, uid)); - node.Children.Add (child); - node = child; - } - } while (true); - - return thread; - } - - /// - /// Parses the threads. - /// - /// The threads. - /// The IMAP engine. - /// The UIDVALIDITY of the folder. - /// Whether or not asynchronous IO methods should be used. - /// The cancellation token. - public static async Task> ParseThreadsAsync (ImapEngine engine, uint uidValidity, bool doAsync, CancellationToken cancellationToken) - { - var threads = new List (); - ImapToken token; - - do { - token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (token.Type == ImapTokenType.Eoln) - break; - - token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); - - ImapEngine.AssertToken (token, ImapTokenType.OpenParen, ImapEngine.GenericUntaggedResponseSyntaxErrorFormat, "THREAD", token); - - threads.Add (await ParseThreadAsync (engine, uidValidity, doAsync, cancellationToken).ConfigureAwait (false)); - } while (true); - - return threads; - } - } -} diff --git a/src/MailKit/Net/NetworkStream.cs b/src/MailKit/Net/NetworkStream.cs deleted file mode 100644 index 6a40302..0000000 --- a/src/MailKit/Net/NetworkStream.cs +++ /dev/null @@ -1,298 +0,0 @@ -// -// NetworkStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Net.Sockets; -using System.Threading.Tasks; - -namespace MailKit.Net -{ - class NetworkStream : Stream - { - SocketAsyncEventArgs send; - SocketAsyncEventArgs recv; - bool ownsSocket; - bool connected; - - public NetworkStream (Socket socket, bool ownsSocket) - { - send = new SocketAsyncEventArgs (); - send.Completed += AsyncOperationCompleted; - send.AcceptSocket = socket; - - recv = new SocketAsyncEventArgs (); - recv.Completed += AsyncOperationCompleted; - recv.AcceptSocket = socket; - - this.ownsSocket = ownsSocket; - connected = socket.Connected; - Socket = socket; - } - - ~NetworkStream () - { - Dispose (false); - } - - public Socket Socket { - get; private set; - } - - public bool DataAvailable { - get { return connected && Socket.Available > 0; } - } - - public override bool CanRead { - get { return connected; } - } - - public override bool CanWrite { - get { return connected; } - } - - public override bool CanSeek { - get { return false; } - } - - public override bool CanTimeout { - get { return connected; } - } - - public override long Length { - get { throw new NotSupportedException (); } - } - - public override long Position { - get { throw new NotSupportedException (); } - set { throw new NotSupportedException (); } - } - - public override int ReadTimeout { - get { - int timeout = Socket.ReceiveTimeout; - - return timeout == 0 ? Timeout.Infinite : timeout; - } - set { - if (value <= 0 && value != Timeout.Infinite) - throw new ArgumentOutOfRangeException (nameof (value)); - - Socket.ReceiveTimeout = value; - } - } - - public override int WriteTimeout { - get { - int timeout = Socket.SendTimeout; - - return timeout == 0 ? Timeout.Infinite : timeout; - } - set { - if (value <= 0 && value != Timeout.Infinite) - throw new ArgumentOutOfRangeException (nameof (value)); - - Socket.SendTimeout = value; - } - } - - void AsyncOperationCompleted (object sender, SocketAsyncEventArgs args) - { - var tcs = (TaskCompletionSource) args.UserToken; - - if (args.SocketError == SocketError.Success) { - tcs.TrySetResult (true); - return; - } - - tcs.TrySetException (new SocketException ((int) args.SocketError)); - } - - void Disconnect () - { - try { - Socket.Dispose (); - } catch { - return; - } finally { - connected = false; - send.Dispose (); - send = null; - recv.Dispose (); - recv = null; - } - } - - public override int Read (byte[] buffer, int offset, int count) - { - try { - return Socket.Receive (buffer, offset, count, SocketFlags.None); - } catch (SocketException ex) { - throw new IOException (ex.Message, ex); - } - } - - public override async Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested (); - - var tcs = new TaskCompletionSource (); - - 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.UserToken = tcs; - - if (!Socket.ReceiveAsync (recv)) - AsyncOperationCompleted (null, recv); - - try { - await tcs.Task.ConfigureAwait (false); - return recv.BytesTransferred; - } catch (OperationCanceledException) { - if (Socket.Connected) - Socket.Shutdown (SocketShutdown.Both); - - Disconnect (); - throw; - } catch (Exception ex) { - Disconnect (); - if (ex is SocketException) - throw new IOException (ex.Message, ex); - throw; - } - } - } - } - } - - public override void Write (byte[] buffer, int offset, int count) - { - try { - Socket.Send (buffer, offset, count, SocketFlags.None); - } catch (SocketException ex) { - throw new IOException (ex.Message, ex); - } - } - - public override async Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested (); - - var tcs = new TaskCompletionSource (); - - 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.UserToken = tcs; - - if (!Socket.SendAsync (send)) - AsyncOperationCompleted (null, send); - - try { - await tcs.Task.ConfigureAwait (false); - } catch (OperationCanceledException) { - if (Socket.Connected) - Socket.Shutdown (SocketShutdown.Both); - - Disconnect (); - throw; - } catch (Exception ex) { - Disconnect (); - if (ex is SocketException) - throw new IOException (ex.Message, ex); - throw; - } - } - } - } - } - - public override void Flush () - { - } - - public override Task FlushAsync (CancellationToken cancellationToken) - { - return Task.FromResult (true); - } - - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - public static NetworkStream Get (Stream stream) - { - if (stream is CompressedStream compressed) - stream = compressed.InnerStream; - - if (stream is SslStream ssl) - stream = ssl.InnerStream; - - return stream as NetworkStream; - } - - public void Poll (SelectMode mode, CancellationToken cancellationToken) - { - if (!cancellationToken.CanBeCanceled) - return; - - do { - cancellationToken.ThrowIfCancellationRequested (); - // wait 1/4 second and then re-check for cancellation - } while (!Socket.Poll (250000, mode)); - - cancellationToken.ThrowIfCancellationRequested (); - } - - protected override void Dispose (bool disposing) - { - if (disposing) { - if (ownsSocket && connected) { - ownsSocket = false; - Disconnect (); - } else { - send?.Dispose (); - send = null; - - recv?.Dispose (); - recv = null; - } - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MailKit/Net/Pop3/AsyncPop3Client.cs b/src/MailKit/Net/Pop3/AsyncPop3Client.cs deleted file mode 100644 index 00a16cf..0000000 --- a/src/MailKit/Net/Pop3/AsyncPop3Client.cs +++ /dev/null @@ -1,1496 +0,0 @@ -// -// AsyncPop3Client.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -using MailKit.Security; - -namespace MailKit.Net.Pop3 -{ - public partial class Pop3Client - { - /// - /// Asynchronously authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// An asynchronous task context. - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// An POP3 protocol error occurred. - /// - public override Task AuthenticateAsync (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)) - { - return AuthenticateAsync (mechanism, true, cancellationToken); - } - - /// - /// Asynchronously authenticates using the supplied credentials. - /// - /// - /// If the POP3 server supports the APOP authentication mechanism, - /// then APOP is used. - /// If the APOP authentication mechanism is not supported and the - /// server supports one or more SASL authentication mechanisms, then - /// the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the USER and PASS commands are used as a - /// fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// In the case of the APOP authentication mechanism, remove it from the - /// property instead. - /// - /// An asynchronous task context. - /// The text encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// An POP3 protocol error occurred. - /// - public override Task AuthenticateAsync (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)) - { - return AuthenticateAsync (encoding, credentials, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified POP3 or POP3/S server. - /// - /// - /// Establishes a connection to the specified POP3 or POP3/S server. - /// If the has a value of 0, then the - /// parameter is used to determine the default port to - /// connect to. The default port used with - /// is 995. All other values will use a default port of 110. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 995, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// - /// - /// - /// - /// An asynchronous task context. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// was set to - /// - /// and the POP3 server does not support the STLS extension. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task ConnectAsync (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified POP3 or POP3/S server using the provided socket. - /// - /// - /// Establishes a connection to the specified POP3 or POP3/S server using - /// the provided socket. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 995, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// An asynchronous task context. - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the POP3 server does not support the STLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task ConnectAsync (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (socket, host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified POP3 or POP3/S server using the provided stream. - /// - /// - /// Establishes a connection to the specified POP3 or POP3/S server using - /// the provided stream. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 995, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// An asynchronous task context. - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the POP3 server does not support the STLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task ConnectAsync (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (stream, host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously disconnect the service. - /// - /// - /// If is true, a QUIT command will be issued in order to disconnect cleanly. - /// - /// - /// - /// - /// An asynchronous task context. - /// If set to true, a QUIT command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - public override Task DisconnectAsync (bool quit, CancellationToken cancellationToken = default (CancellationToken)) - { - return DisconnectAsync (quit, true, cancellationToken); - } - - /// - /// Asynchronously get the message count. - /// - /// - /// Asynchronously gets the message count. - /// - /// The message count. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override async Task GetMessageCountAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - await UpdateMessageCountAsync (true, cancellationToken).ConfigureAwait (false); - - return Count; - } - - /// - /// Ping the POP3 server to keep the connection alive. - /// - /// Mail servers, if left idle for too long, will automatically drop the connection. - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task NoOpAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return NoOpAsync (true, cancellationToken); - } - - /// - /// Asynchronously enable UTF8 mode. - /// - /// - /// The POP3 UTF8 extension allows the client to retrieve messages in the UTF-8 encoding and - /// may also allow the user to authenticate using a UTF-8 encoded username or password. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The has already been authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the UTF8 extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public Task EnableUTF8Async (CancellationToken cancellationToken = default (CancellationToken)) - { - return EnableUTF8Async (true, cancellationToken); - } - - /// - /// Asynchronously get the list of languages supported by the POP3 server. - /// - /// - /// If the POP3 server supports the LANG extension, it is possible to - /// query the list of languages supported by the POP3 server that can - /// be used for error messages. - /// - /// The supported languages. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the LANG extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public Task> GetLanguagesAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetLanguagesAsync (true, cancellationToken); - } - - /// - /// Asynchronously set the language used by the POP3 server for error messages. - /// - /// - /// If the POP3 server supports the LANG extension, it is possible to - /// set the language used by the POP3 server for error messages. - /// - /// An asynchronous task context. - /// The language code. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the LANG extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public Task SetLanguageAsync (string lang, CancellationToken cancellationToken = default (CancellationToken)) - { - return SetLanguageAsync (lang, true, cancellationToken); - } - - /// - /// Asynchronously get the UID of the message at the specified index. - /// - /// - /// Gets the UID of the message at the specified index. - /// Not all servers support UIDs, so you should first check the - /// property for the flag or - /// the convenience property. - /// - /// The message UID. - /// The message index. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task GetMessageUidAsync (int index, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMessageUidAsync (index, true, cancellationToken); - } - - /// - /// Asynchronously get the full list of available message UIDs. - /// - /// - /// Gets the full list of available message UIDs. - /// Not all servers support UIDs, so you should first check the - /// property for the flag or - /// the convenience property. - /// - /// - /// - /// - /// The message uids. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task> GetMessageUidsAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMessageUidsAsync (true, cancellationToken); - } - - /// - /// Asynchronously get the size of the specified message, in bytes. - /// - /// - /// Gets the size of the specified message, in bytes. - /// - /// The message size, in bytes. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task GetMessageSizeAsync (int index, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMessageSizeAsync (index, true, cancellationToken); - } - - /// - /// Asynchronously get the sizes for all available messages, in bytes. - /// - /// - /// Gets the sizes for all available messages, in bytes. - /// - /// The message sizes, in bytes. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task> GetMessageSizesAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMessageSizesAsync (true, cancellationToken); - } - - /// - /// Asynchronously get the headers for the message at the specified index. - /// - /// - /// Gets the headers for the message at the specified index. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task GetMessageHeadersAsync (int index, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - var ctx = new DownloadHeaderContext (this, parser); - - return ctx.DownloadAsync (index + 1, true, true, cancellationToken); - } - - /// - /// Asynchronously get the headers for the messages at the specified indexes. - /// - /// - /// Gets the headers for the messages at the specified indexes. - /// When the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message because - /// it will batch the commands to reduce latency. - /// - /// The headers for the specified messages. - /// The indexes of the messages. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task> GetMessageHeadersAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (indexes.Count == 0) - return Task.FromResult ((IList) new HeaderList[0]); - - var seqids = new int[indexes.Count]; - - for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] < 0 || indexes[i] >= total) - throw new ArgumentException ("One or more of the indexes are invalid.", nameof (indexes)); - - seqids[i] = indexes[i] + 1; - } - - var ctx = new DownloadHeaderContext (this, parser); - - return ctx.DownloadAsync (seqids, true, true, cancellationToken); - } - - /// - /// Asynchronously get the headers of the messages within the specified range. - /// - /// - /// Gets the headers of the messages within the specified range. - /// When the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message because - /// it will batch the commands to reduce latency. - /// - /// The headers of the messages within the specified range. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task> GetMessageHeadersAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (startIndex < 0 || startIndex >= total) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (count < 0 || count > (total - startIndex)) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (count == 0) - return Task.FromResult ((IList) new HeaderList[0]); - - var seqids = new int[count]; - - for (int i = 0; i < count; i++) - seqids[i] = startIndex + i + 1; - - var ctx = new DownloadHeaderContext (this, parser); - - return ctx.DownloadAsync (seqids, true, true, cancellationToken); - } - - /// - /// Asynchronously get the message at the specified index. - /// - /// - /// Gets the message at the specified index. - /// - /// - /// - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task GetMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - var ctx = new DownloadMessageContext (this, parser, progress); - - return ctx.DownloadAsync (index + 1, false, true, cancellationToken); - } - - /// - /// Asynchronously get the messages at the specified indexes. - /// - /// - /// Gets the messages at the specified indexes. - /// When the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message - /// because it will batch the commands to reduce latency. - /// - /// The messages. - /// The indexes of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task> GetMessagesAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (indexes.Count == 0) - return Task.FromResult ((IList) new MimeMessage[0]); - - var seqids = new int[indexes.Count]; - - for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] < 0 || indexes[i] >= total) - throw new ArgumentException ("One or more of the indexes are invalid.", nameof (indexes)); - - seqids[i] = indexes[i] + 1; - } - - var ctx = new DownloadMessageContext (this, parser, progress); - - return ctx.DownloadAsync (seqids, false, true, cancellationToken); - } - - /// - /// Asynchronously get the messages within the specified range. - /// - /// - /// Gets the messages within the specified range. - /// When the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message - /// because it will batch the commands to reduce latency. - /// - /// - /// - /// - /// The messages. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task> GetMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (startIndex < 0 || startIndex >= total) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (count < 0 || count > (total - startIndex)) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (count == 0) - return Task.FromResult ((IList) new MimeMessage[0]); - - var seqids = new int[count]; - - for (int i = 0; i < count; i++) - seqids[i] = startIndex + i + 1; - - var ctx = new DownloadMessageContext (this, parser, progress); - - return ctx.DownloadAsync (seqids, false, true, cancellationToken); - } - - /// - /// Asynchronously get the message or header stream at the specified index. - /// - /// - /// Gets the message or header stream at the specified index. - /// - /// The message or header stream. - /// The index of the message. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task GetStreamAsync (int index, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - var ctx = new DownloadStreamContext (this, progress); - - return ctx.DownloadAsync (index + 1, headersOnly, true, cancellationToken); - } - - /// - /// Asynchronously get the message or header streams at the specified indexes. - /// - /// - /// Get the message or header streams at the specified indexes. - /// If the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message - /// because it will batch the commands to reduce latency. - /// - /// The message or header streams. - /// The indexes of the messages. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task> GetStreamsAsync (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (indexes.Count == 0) - return Task.FromResult ((IList) new Stream[0]); - - var seqids = new int[indexes.Count]; - - for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] < 0 || indexes[i] >= total) - throw new ArgumentException ("One or more of the indexes are invalid.", nameof (indexes)); - - seqids[i] = indexes[i] + 1; - } - - var ctx = new DownloadStreamContext (this, progress); - - return ctx.DownloadAsync (seqids, headersOnly, true, cancellationToken); - } - - /// - /// Asynchronously get the message or header streams within the specified range. - /// - /// - /// Gets the message or header streams within the specified range. - /// If the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message - /// because it will batch the commands to reduce latency. - /// - /// The message or header streams. - /// The index of the first stream to get. - /// The number of streams to get. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task> GetStreamsAsync (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (startIndex < 0 || startIndex >= total) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (count < 0 || count > (total - startIndex)) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (count == 0) - return Task.FromResult ((IList) new Stream[0]); - - var seqids = new int[count]; - - for (int i = 0; i < count; i++) - seqids[i] = startIndex + i + 1; - - var ctx = new DownloadStreamContext (this, progress); - - return ctx.DownloadAsync (seqids, headersOnly, true, cancellationToken); - } - - /// - /// Asynchronously mark the specified message for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// - /// - /// - /// An asynchronous task context. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task DeleteMessageAsync (int index, CancellationToken cancellationToken = default (CancellationToken)) - { - return DeleteMessageAsync (index, true, cancellationToken); - } - - /// - /// Asynchronously mark the specified messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The indexes of the messages. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task DeleteMessagesAsync (IList indexes, CancellationToken cancellationToken = default (CancellationToken)) - { - return DeleteMessagesAsync (indexes, true, cancellationToken); - } - - /// - /// Asynchronously mark the specified range of messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// - /// - /// - /// An asynchronous task context. - /// The index of the first message to mark for deletion. - /// The number of messages to mark for deletion. - /// The cancellation token. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task DeleteMessagesAsync (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)) - { - return DeleteMessagesAsync (startIndex, count, true, cancellationToken); - } - - /// - /// Asynchronously mark all messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override async Task DeleteAllMessagesAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - if (total > 0) - await DeleteMessagesAsync (0, total, cancellationToken).ConfigureAwait (false); - } - - /// - /// Asynchronously reset the state of all messages marked for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// An awaitable task. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Task ResetAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return ResetAsync (true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Pop3/IPop3Client.cs b/src/MailKit/Net/Pop3/IPop3Client.cs deleted file mode 100644 index 1598288..0000000 --- a/src/MailKit/Net/Pop3/IPop3Client.cs +++ /dev/null @@ -1,309 +0,0 @@ -// -// IPop3Client.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MailKit.Net.Pop3 { - /// - /// An interface for a POP3 client. - /// - /// - /// Implemented by . - /// - public interface IPop3Client : IMailSpool - { - /// - /// Gets the capabilities supported by the POP3 server. - /// - /// - /// The capabilities will not be known until a successful connection has been made - /// and may change once the client is authenticated. - /// - /// - /// - /// - /// The capabilities. - /// - /// Capabilities cannot be enabled, they may only be disabled. - /// - Pop3Capabilities Capabilities { get; set; } - - /// - /// Gets the expiration policy. - /// - /// - /// If the server supports the EXPIRE capability (), the value - /// of the property will reflect the value advertized by the server. - /// A value of -1 indicates that messages will never expire. - /// A value of 0 indicates that messages that have been retrieved during the current session - /// will be purged immediately after the connection is closed via the QUIT command. - /// Values larger than 0 indicate the minimum number of days that the server will retain - /// messages which have been retrieved. - /// - /// - /// - /// - /// The expiration policy. - int ExpirePolicy { get; } - - /// - /// Gets the implementation details of the server. - /// - /// - /// If the server advertizes its implementation details, this value will be set to a string containing the - /// information details provided by the server. - /// - /// The implementation details. - string Implementation { get; } - - /// - /// Gets the minimum delay, in milliseconds, between logins. - /// - /// - /// If the server supports the LOGIN-DELAY capability (), this value - /// will be set to the minimum number of milliseconds that the client must wait between logins. - /// - /// - /// - /// - /// The login delay. - int LoginDelay { get; } - - /// - /// Enable UTF8 mode. - /// - /// - /// The POP3 UTF8 extension allows the client to retrieve messages in the UTF-8 encoding and - /// may also allow the user to authenticate using a UTF-8 encoded username or password. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The has already been authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the UTF8 extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - void EnableUTF8 (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously enable UTF8 mode. - /// - /// - /// The POP3 UTF8 extension allows the client to retrieve messages in the UTF-8 encoding and - /// may also allow the user to authenticate using a UTF-8 encoded username or password. - /// - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The has already been authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the UTF8 extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - Task EnableUTF8Async (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Get the list of languages supported by the POP3 server. - /// - /// - /// If the POP3 server supports the LANG extension, it is possible to - /// query the list of languages supported by the POP3 server that can - /// be used for error messages. - /// - /// The supported languages. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the LANG extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - IList GetLanguages (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously get the list of languages supported by the POP3 server. - /// - /// - /// If the POP3 server supports the LANG extension, it is possible to - /// query the list of languages supported by the POP3 server that can - /// be used for error messages. - /// - /// The supported languages. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the LANG extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - Task> GetLanguagesAsync (CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Set the language used by the POP3 server for error messages. - /// - /// - /// If the POP3 server supports the LANG extension, it is possible to - /// set the language used by the POP3 server for error messages. - /// - /// The language code. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the LANG extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - void SetLanguage (string lang, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously set the language used by the POP3 server for error messages. - /// - /// - /// If the POP3 server supports the LANG extension, it is possible to - /// set the language used by the POP3 server for error messages. - /// - /// An asynchronous task context. - /// The language code. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the LANG extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - Task SetLanguageAsync (string lang, CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MailKit/Net/Pop3/Pop3Capabilities.cs b/src/MailKit/Net/Pop3/Pop3Capabilities.cs deleted file mode 100644 index 168aee8..0000000 --- a/src/MailKit/Net/Pop3/Pop3Capabilities.cs +++ /dev/null @@ -1,129 +0,0 @@ -// -// Pop3Capabilities.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Net.Pop3 { - /// - /// Capabilities supported by a POP3 server. - /// - /// - /// Capabilities are read as part of the response to the CAPA command that - /// is issued during the connection and authentication phases of the - /// . - /// - /// - /// - /// - [Flags] - public enum Pop3Capabilities : uint { - /// - /// The server does not support any additional extensions. - /// - None = 0, - - /// - /// The server supports APOP - /// authentication. - /// - Apop = 1 << 0, - - /// - /// The server supports the EXPIRE extension - /// and defines the expiration policy for messages (see ). - /// - Expire = 1 << 1, - - /// - /// The server supports the LOGIN-DELAY extension, - /// allowing the server to specify to the client a minimum number of seconds between login attempts - /// (see ). - /// - LoginDelay = 1 << 2, - - /// - /// The server supports the PIPELINING extension, - /// allowing the client to batch multiple requests to the server at at time. - /// - Pipelining = 1 << 3, - - /// - /// The server supports the RESP-CODES extension, - /// allowing the server to provide clients with extended information in error responses. - /// - ResponseCodes = 1 << 4, - - /// - /// The server supports the SASL authentication - /// extension, allowing the client to authenticate using the advertized authentication mechanisms - /// (see ). - /// - Sasl = 1 << 5, - - /// - /// The server supports the STLS extension, - /// allowing clients to switch to an encrypted SSL/TLS connection after connecting. - /// - StartTLS = 1 << 6, - - /// - /// The server supports the TOP command, - /// allowing clients to fetch the headers plus an arbitrary number of lines. - /// - Top = 1 << 7, - - /// - /// The server supports the UIDL command, - /// allowing the client to refer to messages via a UID as opposed to a sequence ID. - /// - UIDL = 1 << 8, - - /// - /// The server supports the USER - /// authentication command, allowing the client to authenticate via a plain-text username - /// and password command (not recommended unless no other authentication mechanisms exist). - /// - User = 1 << 9, - - /// - /// The server supports the UTF8 extension, - /// allowing clients to retrieve messages in the UTF-8 encoding. - /// - UTF8 = 1 << 10, - - /// - /// The server supports the UTF8=USER extension, - /// allowing clients to authenticate using UTF-8 encoded usernames and passwords. - /// - UTF8User = 1 << 11, - - /// - /// The server supports the LANG extension, - /// allowing clients to specify which language the server should use for error strings. - /// - Lang = 1 << 12, - } -} diff --git a/src/MailKit/Net/Pop3/Pop3Client.cs b/src/MailKit/Net/Pop3/Pop3Client.cs deleted file mode 100644 index cbe1947..0000000 --- a/src/MailKit/Net/Pop3/Pop3Client.cs +++ /dev/null @@ -1,3401 +0,0 @@ -// -// Pop3Client.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Net.Security; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; - -using MimeKit; -using MimeKit.IO; - -using MailKit.Security; - -using SslStream = MailKit.Net.SslStream; -using NetworkStream = MailKit.Net.NetworkStream; - -namespace MailKit.Net.Pop3 { - /// - /// A POP3 client that can be used to retrieve messages from a server. - /// - /// - /// The class supports both the "pop" and "pops" protocols. The "pop" protocol - /// makes a clear-text connection to the POP3 server and does not use SSL or TLS unless the POP3 server - /// supports the STLS extension. The "pops" protocol, - /// however, connects to the POP3 server using an SSL-wrapped connection. - /// - /// - /// - /// - public partial class Pop3Client : MailSpool, IPop3Client - { - [Flags] - enum ProbedCapabilities : byte { - None = 0, - Top = (1 << 0), - UIDL = (1 << 1) - } - - readonly MimeParser parser = new MimeParser (Stream.Null); - readonly Pop3Engine engine; - ProbedCapabilities probed; - bool disposed, disconnecting, secure, utf8; - int timeout = 2 * 60 * 1000; - long octets; - int total; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Before you can retrieve messages with the , you must first call - /// one of the Connect methods - /// and authenticate using one of the - /// Authenticate methods. - /// - /// - /// - /// - /// The protocol logger. - /// - /// is null. - /// - public Pop3Client (IProtocolLogger protocolLogger) : base (protocolLogger) - { - engine = new Pop3Engine (); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Before you can retrieve messages with the , you must first call - /// one of the Connect methods - /// and authenticate using one of the - /// Authenticate methods. - /// - public Pop3Client () : this (new NullProtocolLogger ()) - { - } - - /// - /// Gets an object that can be used to synchronize access to the POP3 server. - /// - /// - /// Gets an object that can be used to synchronize access to the POP3 server. - /// When using the non-Async methods from multiple threads, it is important to lock the - /// object for thread safety when using the synchronous methods. - /// - /// The lock object. - public override object SyncRoot { - get { return engine; } - } - - /// - /// Gets the protocol supported by the message service. - /// - /// - /// Gets the protocol supported by the message service. - /// - /// The protocol. - protected override string Protocol { - get { return "pop"; } - } - - /// - /// Gets the capabilities supported by the POP3 server. - /// - /// - /// The capabilities will not be known until a successful connection has been made - /// and may change once the client is authenticated. - /// - /// - /// - /// - /// The capabilities. - /// - /// Capabilities cannot be enabled, they may only be disabled. - /// - public Pop3Capabilities Capabilities { - get { return engine.Capabilities; } - set { - if ((engine.Capabilities | value) > engine.Capabilities) - throw new ArgumentException ("Capabilities cannot be enabled, they may only be disabled.", nameof (value)); - - engine.Capabilities = value; - } - } - - /// - /// Gets the expiration policy. - /// - /// - /// If the server supports the EXPIRE capability (), the value - /// of the property will reflect the value advertized by the server. - /// A value of -1 indicates that messages will never expire. - /// A value of 0 indicates that messages that have been retrieved during the current session - /// will be purged immediately after the connection is closed via the QUIT command. - /// Values larger than 0 indicate the minimum number of days that the server will retain - /// messages which have been retrieved. - /// - /// - /// - /// - /// The expiration policy. - public int ExpirePolicy { - get { return engine.ExpirePolicy; } - } - - /// - /// Gets the implementation details of the server. - /// - /// - /// If the server advertizes its implementation details, this value will be set to a string containing the - /// information details provided by the server. - /// - /// The implementation details. - public string Implementation { - get { return engine.Implementation; } - } - - /// - /// Gets the minimum delay, in milliseconds, between logins. - /// - /// - /// If the server supports the LOGIN-DELAY capability (), this value - /// will be set to the minimum number of milliseconds that the client must wait between logins. - /// - /// - /// - /// - /// The login delay. - public int LoginDelay { - get { return engine.LoginDelay; } - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (Pop3Client)); - } - - void CheckConnected () - { - if (!IsConnected) - throw new ServiceNotConnectedException ("The Pop3Client is not connected."); - } - - void CheckAuthenticated () - { - if (!IsAuthenticated) - throw new ServiceNotAuthenticatedException ("The Pop3Client has not been authenticated."); - } - - bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { - if (ServerCertificateValidationCallback != null) - return ServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (ServicePointManager.ServerCertificateValidationCallback != null) - return ServicePointManager.ServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); -#endif - - return DefaultServerCertificateValidationCallback (engine.Uri.Host, certificate, chain, sslPolicyErrors); - } - - static Exception CreatePop3Exception (Pop3Command pc) - { - var command = pc.Command.Split (' ')[0].TrimEnd (); - 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 Pop3ProtocolException (message); - } - - static ProtocolException CreatePop3ParseException (Exception innerException, string format, params object[] args) - { - return new Pop3ProtocolException (string.Format (CultureInfo.InvariantCulture, format, args), innerException); - } - - static ProtocolException CreatePop3ParseException (string format, params object[] args) - { - return new Pop3ProtocolException (string.Format (CultureInfo.InvariantCulture, format, args)); - } - - async Task SendCommandAsync (bool doAsync, CancellationToken token, string command) - { - var pc = engine.QueueCommand (token, null, Encoding.ASCII, command); - int id; - - do { - if (doAsync) - id = await engine.IterateAsync ().ConfigureAwait (false); - else - id = engine.Iterate (); - } while (id < pc.Id); - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - } - - Task SendCommandAsync (bool doAsync, CancellationToken token, string format, params object[] args) - { - return SendCommandAsync (doAsync, token, Encoding.ASCII, format, args); - } - - async Task SendCommandAsync (bool doAsync, CancellationToken token, Encoding encoding, string format, params object[] args) - { - string okText = string.Empty; - int id; - - var pc = engine.QueueCommand (token, (pop3, cmd, text, xdoAsync) => { - if (cmd.Status == Pop3CommandStatus.Ok) - okText = text; - - return Task.FromResult (true); - }, encoding, format, args); - - do { - if (doAsync) - id = await engine.IterateAsync ().ConfigureAwait (false); - else - id = engine.Iterate (); - } while (id < pc.Id); - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - - return okText; - } - - #region IMailService implementation - - /// - /// Gets the authentication mechanisms supported by the POP3 server. - /// - /// - /// The authentication mechanisms are queried as part of the - /// connection process. - /// Servers that do not support the SASL capability will typically - /// support either the APOP authentication mechanism - /// () or the ability to login using the - /// USER and PASS commands (). - /// - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before authenticating. - /// In the case of the APOP authentication mechanism, remove it from the - /// property instead. - /// - /// - /// - /// - /// The authentication mechanisms. - public override HashSet AuthenticationMechanisms { - get { return engine.AuthenticationMechanisms; } - } - - /// - /// Gets or sets the timeout for network streaming operations, in milliseconds. - /// - /// - /// Gets or sets the underlying socket stream's - /// and values. - /// - /// The timeout in milliseconds. - public override int Timeout { - get { return timeout; } - set { - if (IsConnected && engine.Stream.CanTimeout) { - engine.Stream.WriteTimeout = value; - engine.Stream.ReadTimeout = value; - } - - timeout = value; - } - } - - /// - /// Gets whether or not the client is currently connected to an POP3 server. - /// - /// - /// The state is set to true immediately after - /// one of the Connect - /// methods succeeds and is not set back to false until either the client - /// is disconnected via or until a - /// is thrown while attempting to read or write to - /// the underlying network socket. - /// When an is caught, the connection state of the - /// should be checked before continuing. - /// - /// - /// - /// - /// true if the client is connected; otherwise, false. - public override bool IsConnected { - get { return engine.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). - /// - /// true if the connection is secure; otherwise, false. - public override bool IsSecure { - get { return IsConnected && secure; } - } - - /// - /// Get whether or not the client is currently authenticated with the POP3 server. - /// - /// - /// Gets whether or not the client is currently authenticated with the POP3 server. - /// To authenticate with the POP3 server, use one of the - /// Authenticate methods. - /// - /// true if the client is connected; otherwise, false. - public override bool IsAuthenticated { - get { return engine.State == Pop3EngineState.Transaction; } - } - - async Task UpdateMessageCountAsync (bool doAsync, CancellationToken cancellationToken) - { - var pc = engine.QueueCommand (cancellationToken, (pop3, cmd, text, xdoAsync) => { - if (cmd.Status != Pop3CommandStatus.Ok) - return Task.FromResult (false); - - // the response should be " " - var tokens = text.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - - if (tokens.Length < 2) { - cmd.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the STAT command: {0}", text); - return Task.FromResult (false); - } - - if (!int.TryParse (tokens[0], NumberStyles.None, CultureInfo.InvariantCulture, out total) || total < 0) { - cmd.Exception = CreatePop3ParseException ("Pop3 server returned an invalid response to the STAT command: {0}", text); - return Task.FromResult (false); - } - - if (!long.TryParse (tokens[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out octets)) { - cmd.Exception = CreatePop3ParseException ("Pop3 server returned an invalid response to the STAT command: {0}", text); - return Task.FromResult (false); - } - - return Task.FromResult (true); - }, "STAT"); - int id; - - do { - if (doAsync) - id = await engine.IterateAsync ().ConfigureAwait (false); - else - id = engine.Iterate (); - } while (id < pc.Id); - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - - if (pc.Exception != null) - throw pc.Exception; - } - - async Task ProbeCapabilitiesAsync (bool doAsync, CancellationToken cancellationToken) - { - if ((engine.Capabilities & Pop3Capabilities.UIDL) == 0 && (probed & ProbedCapabilities.UIDL) == 0) { - // if the message count is > 0, we can probe the UIDL command - if (total > 0) { - try { - var ctx = new MessageUidContext (this, 1); - - await ctx.GetUidAsync (doAsync, cancellationToken).ConfigureAwait (false); - } catch (NotSupportedException) { - } - } - } - } - - async Task QueryCapabilitiesAsync (bool doAsync, CancellationToken cancellationToken) - { - if (doAsync) - await engine.QueryCapabilitiesAsync (cancellationToken).ConfigureAwait (false); - else - engine.QueryCapabilities (cancellationToken); - } - - class SaslAuthContext - { - readonly SaslMechanism mechanism; - readonly Pop3Client client; - - public SaslAuthContext (Pop3Client client, SaslMechanism mechanism) - { - this.mechanism = mechanism; - this.client = client; - } - - public string AuthMessage { - get; private set; - } - - Pop3Engine Engine { - get { return client.engine; } - } - - async Task OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, bool doAsync) - { - while (pc.Status == Pop3CommandStatus.Continue && !mechanism.IsAuthenticated) { - var challenge = mechanism.Challenge (text); - - var buf = Encoding.ASCII.GetBytes (challenge + "\r\n"); - string response; - - if (doAsync) { - await pop3.Stream.WriteAsync (buf, 0, buf.Length, pc.CancellationToken).ConfigureAwait (false); - await pop3.Stream.FlushAsync (pc.CancellationToken).ConfigureAwait (false); - - response = (await pop3.ReadLineAsync (pc.CancellationToken).ConfigureAwait (false)).TrimEnd (); - } else { - pop3.Stream.Write (buf, 0, buf.Length, pc.CancellationToken); - pop3.Stream.Flush (pc.CancellationToken); - - response = pop3.ReadLine (pc.CancellationToken).TrimEnd (); - } - - pc.Status = Pop3Engine.GetCommandStatus (response, out text); - pc.StatusText = text; - - if (pc.Status == Pop3CommandStatus.ProtocolError) - throw new Pop3ProtocolException (string.Format ("Unexpected response from server: {0}", response)); - } - - AuthMessage = text; - } - - public async Task AuthenticateAsync (bool doAsync, CancellationToken cancellationToken) - { - var pc = Engine.QueueCommand (cancellationToken, OnDataReceived, "AUTH {0}", mechanism.MechanismName); - int id; - - AuthMessage = string.Empty; - - do { - if (doAsync) - id = await Engine.IterateAsync ().ConfigureAwait (false); - else - id = Engine.Iterate (); - } while (id < pc.Id); - - return pc; - } - } - - async Task AuthenticateAsync (SaslMechanism mechanism, bool doAsync, CancellationToken cancellationToken) - { - if (mechanism == null) - throw new ArgumentNullException (nameof (mechanism)); - - if (!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 (); - - mechanism.Uri = new Uri ("pop://" + engine.Uri.Host); - - var ctx = new SaslAuthContext (this, mechanism); - var pc = await ctx.AuthenticateAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (pc.Status == Pop3CommandStatus.Error) - throw new AuthenticationException (); - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - - if (pc.Exception != null) - throw pc.Exception; - - engine.State = Pop3EngineState.Transaction; - - await QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - await UpdateMessageCountAsync (doAsync, cancellationToken).ConfigureAwait (false); - await ProbeCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - OnAuthenticated (ctx.AuthMessage); - } - - /// - /// Authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// An POP3 protocol error occurred. - /// - public override void Authenticate (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)) - { - AuthenticateAsync (mechanism, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task AuthenticateAsync (Encoding encoding, ICredentials credentials, bool doAsync, CancellationToken cancellationToken) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (credentials == null) - throw new ArgumentNullException (nameof (credentials)); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The Pop3Client must be connected before you can authenticate."); - - if (IsAuthenticated) - throw new InvalidOperationException ("The Pop3Client is already authenticated."); - - CheckDisposed (); - - var saslUri = new Uri ("pop://" + engine.Uri.Host); - string userName, password, message = null; - NetworkCredential cred; - - if ((engine.Capabilities & Pop3Capabilities.Apop) != 0) { - cred = credentials.GetCredential (saslUri, "APOP"); - userName = utf8 ? SaslMechanism.SaslPrep (cred.UserName) : cred.UserName; - password = utf8 ? SaslMechanism.SaslPrep (cred.Password) : cred.Password; - var challenge = engine.ApopToken + password; - var md5sum = new StringBuilder (); - byte[] digest; - - using (var md5 = MD5.Create ()) - digest = md5.ComputeHash (encoding.GetBytes (challenge)); - - for (int i = 0; i < digest.Length; i++) - md5sum.Append (digest[i].ToString ("x2")); - - try { - message = await SendCommandAsync (doAsync, cancellationToken, encoding, "APOP {0} {1}", userName, md5sum).ConfigureAwait (false); - engine.State = Pop3EngineState.Transaction; - } catch (Pop3CommandException) { - } - - if (engine.State == Pop3EngineState.Transaction) { - await QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - await UpdateMessageCountAsync (doAsync, cancellationToken).ConfigureAwait (false); - await ProbeCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - OnAuthenticated (message ?? string.Empty); - return; - } - } - - if ((engine.Capabilities & Pop3Capabilities.Sasl) != 0) { - foreach (var authmech in SaslMechanism.AuthMechanismRank) { - SaslMechanism sasl; - - if (!engine.AuthenticationMechanisms.Contains (authmech)) - continue; - - if ((sasl = SaslMechanism.Create (authmech, saslUri, encoding, credentials)) == null) - continue; - - cancellationToken.ThrowIfCancellationRequested (); - - var ctx = new SaslAuthContext (this, sasl); - var pc = await ctx.AuthenticateAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (pc.Status == Pop3CommandStatus.Error) - continue; - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - - if (pc.Exception != null) - throw pc.Exception; - - engine.State = Pop3EngineState.Transaction; - - await QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - await UpdateMessageCountAsync (doAsync, cancellationToken).ConfigureAwait (false); - await ProbeCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - OnAuthenticated (ctx.AuthMessage); - return; - } - } - - // fall back to the classic USER & PASS commands... - cred = credentials.GetCredential (saslUri, "DEFAULT"); - userName = utf8 ? SaslMechanism.SaslPrep (cred.UserName) : cred.UserName; - password = utf8 ? SaslMechanism.SaslPrep (cred.Password) : cred.Password; - - try { - await SendCommandAsync (doAsync, cancellationToken, encoding, "USER {0}", userName).ConfigureAwait (false); - message = await SendCommandAsync (doAsync, cancellationToken, encoding, "PASS {0}", password).ConfigureAwait (false); - } catch (Pop3CommandException) { - throw new AuthenticationException (); - } - - engine.State = Pop3EngineState.Transaction; - - await QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - await UpdateMessageCountAsync (doAsync, cancellationToken).ConfigureAwait (false); - await ProbeCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - OnAuthenticated (message); - } - - /// - /// Authenticate using the supplied credentials. - /// - /// - /// If the POP3 server supports the APOP authentication mechanism, - /// then APOP is used. - /// If the APOP authentication mechanism is not supported and the - /// server supports one or more SASL authentication mechanisms, then - /// the SASL mechanisms that both the client and server support are tried - /// in order of greatest security to weakest security. Once a SASL - /// authentication mechanism is found that both client and server support, - /// the credentials are used to authenticate. - /// If the server does not support SASL or if no common SASL mechanisms - /// can be found, then the USER and PASS commands are used as a - /// fallback. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// In the case of the APOP authentication mechanism, remove it from the - /// property instead. - /// - /// The text encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// An POP3 protocol error occurred. - /// - public override void Authenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)) - { - AuthenticateAsync (encoding, credentials, false, cancellationToken).GetAwaiter ().GetResult (); - } - - internal void ReplayConnect (string host, Stream replayStream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (replayStream == null) - throw new ArgumentNullException (nameof (replayStream)); - - CheckDisposed (); - - probed = ProbedCapabilities.None; - secure = false; - - engine.Uri = new Uri ($"pop://{host}:110"); - engine.Connect (new Pop3Stream (replayStream, ProtocolLogger), cancellationToken); - engine.QueryCapabilities (cancellationToken); - engine.Disconnected += OnEngineDisconnected; - OnConnected (host, 110, SecureSocketOptions.None); - } - - internal async Task ReplayConnectAsync (string host, Stream replayStream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (replayStream == null) - throw new ArgumentNullException (nameof (replayStream)); - - CheckDisposed (); - - probed = ProbedCapabilities.None; - secure = false; - - engine.Uri = new Uri ($"pop://{host}:110"); - await engine.ConnectAsync (new Pop3Stream (replayStream, ProtocolLogger), cancellationToken).ConfigureAwait (false); - await engine.QueryCapabilitiesAsync (cancellationToken).ConfigureAwait (false); - engine.Disconnected += OnEngineDisconnected; - OnConnected (host, 110, SecureSocketOptions.None); - } - - internal static void ComputeDefaultValues (string host, ref int port, ref SecureSocketOptions options, out Uri uri, out bool starttls) - { - switch (options) { - default: - if (port == 0) - port = 110; - break; - case SecureSocketOptions.Auto: - switch (port) { - case 0: port = 110; goto default; - case 995: options = SecureSocketOptions.SslOnConnect; break; - default: options = SecureSocketOptions.StartTlsWhenAvailable; break; - } - break; - case SecureSocketOptions.SslOnConnect: - if (port == 0) - port = 995; - break; - } - - switch (options) { - case SecureSocketOptions.StartTlsWhenAvailable: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "pop://{0}:{1}/?starttls=when-available", host, port)); - starttls = true; - break; - case SecureSocketOptions.StartTls: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "pop://{0}:{1}/?starttls=always", host, port)); - starttls = true; - break; - case SecureSocketOptions.SslOnConnect: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "pops://{0}:{1}", host, port)); - starttls = false; - break; - default: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "pop://{0}:{1}", host, port)); - starttls = false; - break; - } - } - - async Task ConnectAsync (string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0) - throw new ArgumentException ("The host name cannot be empty.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - CheckDisposed (); - - if (IsConnected) - throw new InvalidOperationException ("The Pop3Client is already connected."); - - Stream stream; - bool starttls; - Uri uri; - - ComputeDefaultValues (host, ref port, ref options, out uri, out starttls); - - var socket = await ConnectSocket (host, port, doAsync, cancellationToken).ConfigureAwait (false); - - engine.Uri = uri; - - if (options == SecureSocketOptions.SslOnConnect) { - var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); - - try { - if (doAsync) { - await ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - ssl.Dispose (); - - throw SslHandshakeException.Create (this, ex, false); - } - - secure = true; - stream = ssl; - } else { - stream = new NetworkStream (socket, true); - secure = false; - } - - probed = ProbedCapabilities.None; - if (stream.CanTimeout) { - stream.WriteTimeout = timeout; - stream.ReadTimeout = timeout; - } - - try { - ProtocolLogger.LogConnect (uri); - } catch { - stream.Dispose (); - secure = false; - throw; - } - - var pop3 = new Pop3Stream (stream, ProtocolLogger); - - if (doAsync) - await engine.ConnectAsync (pop3, cancellationToken).ConfigureAwait (false); - else - engine.Connect (pop3, cancellationToken); - - try { - await QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (options == SecureSocketOptions.StartTls && (engine.Capabilities & Pop3Capabilities.StartTLS) == 0) - throw new NotSupportedException ("The POP3 server does not support the STLS extension."); - - if (starttls && (engine.Capabilities & Pop3Capabilities.StartTLS) != 0) { - await SendCommandAsync (doAsync, cancellationToken, "STLS").ConfigureAwait (false); - - try { - var tls = new SslStream (stream, false, ValidateRemoteCertificate); - engine.Stream.Stream = tls; - - if (doAsync) { - await tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - throw SslHandshakeException.Create (this, ex, true); - } - - secure = true; - - // re-issue a CAPA command - await QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - } catch { - engine.Disconnect (); - secure = false; - throw; - } - - engine.Disconnected += OnEngineDisconnected; - OnConnected (host, port, options); - } - - /// - /// Establish a connection to the specified POP3 or POP3/S server. - /// - /// - /// Establishes a connection to the specified POP3 or POP3/S server. - /// If the has a value of 0, then the - /// parameter is used to determine the default port to - /// connect to. The default port used with - /// is 995. All other values will use a default port of 110. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 995, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// - /// - /// - /// - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// was set to - /// - /// and the POP3 server does not support the STLS extension. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task ConnectAsync (Stream stream, string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0) - throw new ArgumentException ("The host name cannot be empty.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - CheckDisposed (); - - if (IsConnected) - throw new InvalidOperationException ("The Pop3Client is already connected."); - - Stream network; - bool starttls; - Uri uri; - - ComputeDefaultValues (host, ref port, ref options, out uri, out starttls); - - engine.Uri = uri; - - if (options == SecureSocketOptions.SslOnConnect) { - var ssl = new SslStream (stream, false, ValidateRemoteCertificate); - - try { - if (doAsync) { - await ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - ssl.Dispose (); - - throw SslHandshakeException.Create (this, ex, false); - } - - network = ssl; - secure = true; - } else { - network = stream; - secure = false; - } - - probed = ProbedCapabilities.None; - if (network.CanTimeout) { - network.WriteTimeout = timeout; - network.ReadTimeout = timeout; - } - - try { - ProtocolLogger.LogConnect (uri); - } catch { - network.Dispose (); - secure = false; - throw; - } - - var pop3 = new Pop3Stream (network, ProtocolLogger); - - if (doAsync) - await engine.ConnectAsync (pop3, cancellationToken).ConfigureAwait (false); - else - engine.Connect (pop3, cancellationToken); - - try { - await QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (options == SecureSocketOptions.StartTls && (engine.Capabilities & Pop3Capabilities.StartTLS) == 0) - throw new NotSupportedException ("The POP3 server does not support the STLS extension."); - - if (starttls && (engine.Capabilities & Pop3Capabilities.StartTLS) != 0) { - await SendCommandAsync (doAsync, cancellationToken, "STLS").ConfigureAwait (false); - - var tls = new SslStream (network, false, ValidateRemoteCertificate); - engine.Stream.Stream = tls; - - try { - if (doAsync) { - await tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - throw SslHandshakeException.Create (this, ex, true); - } - - secure = true; - - // re-issue a CAPA command - await QueryCapabilitiesAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - } catch { - engine.Disconnect (); - secure = false; - throw; - } - - engine.Disconnected += OnEngineDisconnected; - OnConnected (host, port, options); - } - - Task ConnectAsync (Socket socket, string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (socket == null) - throw new ArgumentNullException (nameof (socket)); - - if (!socket.Connected) - throw new ArgumentException ("The socket is not connected.", nameof (socket)); - - return ConnectAsync (new NetworkStream (socket, true), host, port, options, doAsync, cancellationToken); - } - - /// - /// Establish a connection to the specified POP3 or POP3/S server using the provided socket. - /// - /// - /// Establishes a connection to the specified POP3 or POP3/S server using - /// the provided socket. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 995, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the POP3 server does not support the STLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void Connect (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (socket, host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Establish a connection to the specified POP3 or POP3/S server using the provided stream. - /// - /// - /// Establishes a connection to the specified POP3 or POP3/S server using - /// the provided stream. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 995, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the POP3 server does not support the STLS extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void Connect (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (stream, host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task DisconnectAsync (bool quit, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (!engine.IsConnected) - return; - - if (quit) { - try { - await SendCommandAsync (doAsync, cancellationToken, "QUIT").ConfigureAwait (false); - } catch (OperationCanceledException) { - } catch (Pop3ProtocolException) { - } catch (Pop3CommandException) { - } catch (IOException) { - } - } - - disconnecting = true; - engine.Disconnect (); - } - - /// - /// Disconnect the service. - /// - /// - /// If is true, a QUIT command will be issued in order to disconnect cleanly. - /// - /// - /// - /// - /// If set to true, a QUIT command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - public override void Disconnect (bool quit, CancellationToken cancellationToken = default (CancellationToken)) - { - DisconnectAsync (quit, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the message count. - /// - /// - /// Gets the message count. - /// - /// The message count. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override int GetMessageCount (CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - UpdateMessageCountAsync (false, cancellationToken).GetAwaiter ().GetResult (); - - return Count; - } - - Task NoOpAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - return SendCommandAsync (doAsync, cancellationToken, "NOOP"); - } - - /// - /// Ping the POP3 server to keep the connection alive. - /// - /// Mail servers, if left idle for too long, will automatically drop the connection. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void NoOp (CancellationToken cancellationToken = default (CancellationToken)) - { - NoOpAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - void OnEngineDisconnected (object sender, EventArgs e) - { - var options = SecureSocketOptions.None; - bool requested = disconnecting; - string host = null; - int port = 0; - - if (engine.Uri != null) { - options = GetSecureSocketOptions (engine.Uri); - host = engine.Uri.Host; - port = engine.Uri.Port; - } - - engine.Disconnected -= OnEngineDisconnected; - disconnecting = secure = utf8 = false; - octets = total = 0; - engine.Uri = null; - - if (host != null) - OnDisconnected (host, port, options, requested); - } - - #endregion - - async Task EnableUTF8Async (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - - if (engine.State != Pop3EngineState.Connected) - throw new InvalidOperationException ("You must enable UTF-8 mode before authenticating."); - - if ((engine.Capabilities & Pop3Capabilities.UTF8) == 0) - throw new NotSupportedException ("The POP3 server does not support the UTF8 extension."); - - if (utf8) - return; - - await SendCommandAsync (doAsync, cancellationToken, "UTF8").ConfigureAwait (false); - utf8 = true; - } - - /// - /// Enable UTF8 mode. - /// - /// - /// The POP3 UTF8 extension allows the client to retrieve messages in the UTF-8 encoding and - /// may also allow the user to authenticate using a UTF-8 encoded username or password. - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The has already been authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the UTF8 extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public void EnableUTF8 (CancellationToken cancellationToken = default (CancellationToken)) - { - EnableUTF8Async (false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task> GetLanguagesAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - - if ((Capabilities & Pop3Capabilities.Lang) == 0) - throw new NotSupportedException ("The POP3 server does not support the LANG extension."); - - var langs = new List (); - - var pc = engine.QueueCommand (cancellationToken, async (pop3, cmd, text, xdoAsync) => { - if (cmd.Status != Pop3CommandStatus.Ok) - return; - - do { - string response; - - if (xdoAsync) - response = await engine.ReadLineAsync (cmd.CancellationToken).ConfigureAwait (false); - else - response = engine.ReadLine (cmd.CancellationToken); - - if (response == ".") - break; - - var tokens = response.Split (new [] { ' ' }, 2); - if (tokens.Length != 2) - continue; - - langs.Add (new Pop3Language (tokens[0], tokens[1])); - } while (true); - }, "LANG"); - int id; - - do { - if (doAsync) - id = await engine.IterateAsync ().ConfigureAwait (false); - else - id = engine.Iterate (); - } while (id < pc.Id); - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - - if (pc.Exception != null) - throw pc.Exception; - - return new ReadOnlyCollection (langs); - } - - /// - /// Get the list of languages supported by the POP3 server. - /// - /// - /// If the POP3 server supports the LANG extension, it is possible to - /// query the list of languages supported by the POP3 server that can - /// be used for error messages. - /// - /// The supported languages. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the LANG extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public IList GetLanguages (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetLanguagesAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - Task SetLanguageAsync (string lang, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - - if (lang == null) - throw new ArgumentNullException (nameof (lang)); - - if (lang.Length == 0) - throw new ArgumentException ("The language code cannot be empty.", nameof (lang)); - - if ((Capabilities & Pop3Capabilities.Lang) == 0) - throw new NotSupportedException ("The POP3 server does not support the LANG extension."); - - return SendCommandAsync (doAsync, cancellationToken, "LANG {0}", lang); - } - - /// - /// Set the language used by the POP3 server for error messages. - /// - /// - /// If the POP3 server supports the LANG extension, it is possible to - /// set the language used by the POP3 server for error messages. - /// - /// The language code. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The POP3 server does not support the LANG extension. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public void SetLanguage (string lang, CancellationToken cancellationToken = default (CancellationToken)) - { - SetLanguageAsync (lang, false, cancellationToken).GetAwaiter ().GetResult (); - } - - #region IMailSpool implementation - - /// - /// Get the number of messages available in the message spool. - /// - /// - /// Gets the number of messages available on the POP3 server. - /// Once authenticated, the property will be set - /// to the number of available messages on the POP3 server. - /// - /// - /// - /// - /// The message count. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - public override int Count { - get { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - return total; - } - } - - /// - /// Gets whether or not the supports referencing messages by UIDs. - /// - /// - /// Not all servers support referencing messages by UID, so this property should - /// be checked before using - /// and . - /// If the server does not support UIDs, then all methods that take UID arguments - /// along with and - /// will fail. - /// - /// true if supports UIDs; otherwise, false. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - public override bool SupportsUids { - get { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - return (engine.Capabilities & Pop3Capabilities.UIDL) != 0; - } - } - - class MessageUidContext - { - readonly Pop3Client client; - readonly int seqid; - string uid; - - public MessageUidContext (Pop3Client client, int seqid) - { - this.client = client; - this.seqid = seqid; - } - - Pop3Engine Engine { - get { return client.engine; } - } - - Task OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, bool doAsync) - { - if (pc.Status != Pop3CommandStatus.Ok) - return Task.FromResult (true); - - var tokens = text.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - int id; - - if (tokens.Length < 2) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the UIDL command."); - return Task.FromResult (true); - } - - if (!int.TryParse (tokens[0], NumberStyles.None, CultureInfo.InvariantCulture, out id) || id != seqid) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an unexpected response to the UIDL command."); - return Task.FromResult (true); - } - - uid = tokens[1]; - - return Task.FromResult (true); - } - - public async Task GetUidAsync (bool doAsync, CancellationToken cancellationToken) - { - var pc = Engine.QueueCommand (cancellationToken, OnDataReceived, "UIDL {0}", seqid.ToString (CultureInfo.InvariantCulture)); - int id; - - do { - if (doAsync) - id = await Engine.IterateAsync ().ConfigureAwait (false); - else - id = Engine.Iterate (); - } while (id < pc.Id); - - client.probed |= ProbedCapabilities.UIDL; - - if (pc.Status != Pop3CommandStatus.Ok) { - if (!client.SupportsUids) - throw new NotSupportedException ("The POP3 server does not support the UIDL extension."); - - throw CreatePop3Exception (pc); - } - - if (pc.Exception != null) - throw pc.Exception; - - Engine.Capabilities |= Pop3Capabilities.UIDL; - - return uid; - } - } - - Task GetMessageUidAsync (int index, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (!SupportsUids && (probed & ProbedCapabilities.UIDL) != 0) - throw new NotSupportedException ("The POP3 server does not support the UIDL extension."); - - var ctx = new MessageUidContext (this, index + 1); - - return ctx.GetUidAsync (doAsync, cancellationToken); - } - - /// - /// Get the UID of the message at the specified index. - /// - /// - /// Gets the UID of the message at the specified index. - /// Not all servers support UIDs, so you should first check the - /// property for the flag or - /// the convenience property. - /// - /// The message UID. - /// The message index. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override string GetMessageUid (int index, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMessageUidAsync (index, false, cancellationToken).GetAwaiter ().GetResult (); - } - - class MessageUidsContext - { - readonly Pop3Client client; - readonly List uids; - - public MessageUidsContext (Pop3Client client) - { - uids = new List (); - this.client = client; - } - - Pop3Engine Engine { - get { return client.engine; } - } - - async Task OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, bool doAsync) - { - if (pc.Status != Pop3CommandStatus.Ok) - return; - - do { - string response; - - if (doAsync) - response = await Engine.ReadLineAsync (pc.CancellationToken).ConfigureAwait (false); - else - response = Engine.ReadLine (pc.CancellationToken); - - if (response == ".") - break; - - if (pc.Exception != null) - continue; - - var tokens = response.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - int seqid; - - if (tokens.Length < 2) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the UIDL command."); - continue; - } - - if (!int.TryParse (tokens[0], NumberStyles.None, CultureInfo.InvariantCulture, out seqid) || seqid != uids.Count + 1) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an invalid response to the UIDL command."); - continue; - } - - uids.Add (tokens[1]); - } while (true); - } - - public async Task> GetUidsAsync (bool doAsync, CancellationToken cancellationToken) - { - var pc = Engine.QueueCommand (cancellationToken, OnDataReceived, "UIDL"); - int id; - - do { - if (doAsync) - id = await Engine.IterateAsync ().ConfigureAwait (false); - else - id = Engine.Iterate (); - } while (id < pc.Id); - - client.probed |= ProbedCapabilities.UIDL; - - if (pc.Status != Pop3CommandStatus.Ok) { - if (!client.SupportsUids) - throw new NotSupportedException ("The POP3 server does not support the UIDL extension."); - - throw CreatePop3Exception (pc); - } - - if (pc.Exception != null) - throw pc.Exception; - - Engine.Capabilities |= Pop3Capabilities.UIDL; - - return uids; - } - } - - Task> GetMessageUidsAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (!SupportsUids && (probed & ProbedCapabilities.UIDL) != 0) - throw new NotSupportedException ("The POP3 server does not support the UIDL extension."); - - var ctx = new MessageUidsContext (this); - - return ctx.GetUidsAsync (doAsync, cancellationToken); - } - - /// - /// Get the full list of available message UIDs. - /// - /// - /// Gets the full list of available message UIDs. - /// Not all servers support UIDs, so you should first check the - /// property for the flag or - /// the convenience property. - /// - /// - /// - /// - /// The message uids. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IList GetMessageUids (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMessageUidsAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - class MessageSizeContext - { - readonly Pop3Client client; - readonly int seqid; - int size; - - public MessageSizeContext (Pop3Client client, int seqid) - { - this.client = client; - this.seqid = seqid; - } - - Pop3Engine Engine { - get { return client.engine; } - } - - Task OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, bool doAsync) - { - if (pc.Status != Pop3CommandStatus.Ok) - return Task.FromResult (true); - - var tokens = text.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - int id; - - if (tokens.Length < 2) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the LIST command: {0}", text); - return Task.FromResult (true); - } - - if (!int.TryParse (tokens[0], NumberStyles.None, CultureInfo.InvariantCulture, out id) || id != seqid) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an unexpected sequence-id token to the LIST command: {0}", tokens[0]); - return Task.FromResult (true); - } - - if (!int.TryParse (tokens[1], NumberStyles.None, CultureInfo.InvariantCulture, out size) || size < 0) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an unexpected size token to the LIST command: {0}", tokens[1]); - return Task.FromResult (true); - } - - return Task.FromResult (true); - } - - public async Task GetSizeAsync (bool doAsync, CancellationToken cancellationToken) - { - var pc = Engine.QueueCommand (cancellationToken, OnDataReceived, "LIST {0}", seqid.ToString (CultureInfo.InvariantCulture)); - int id; - - do { - if (doAsync) - id = await Engine.IterateAsync ().ConfigureAwait (false); - else - id = Engine.Iterate (); - } while (id < pc.Id); - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - - if (pc.Exception != null) - throw pc.Exception; - - return size; - } - } - - Task GetMessageSizeAsync (int index, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - var ctx = new MessageSizeContext (this, index + 1); - - return ctx.GetSizeAsync (doAsync, cancellationToken); - } - - /// - /// Get the size of the specified message, in bytes. - /// - /// - /// Gets the size of the specified message, in bytes. - /// - /// The message size, in bytes. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override int GetMessageSize (int index, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMessageSizeAsync (index, false, cancellationToken).GetAwaiter ().GetResult (); - } - - class MessageSizesContext - { - readonly Pop3Client client; - readonly List sizes; - - public MessageSizesContext (Pop3Client client) - { - sizes = new List (); - this.client = client; - } - - Pop3Engine Engine { - get { return client.engine; } - } - - async Task OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, bool doAsync) - { - if (pc.Status != Pop3CommandStatus.Ok) - return; - - do { - string response; - - if (doAsync) - response = await Engine.ReadLineAsync (pc.CancellationToken).ConfigureAwait (false); - else - response = Engine.ReadLine (pc.CancellationToken); - - if (response == ".") - break; - - if (pc.Exception != null) - continue; - - var tokens = response.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - int seqid, size; - - if (tokens.Length < 2) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the LIST command: {0}", response); - continue; - } - - if (!int.TryParse (tokens[0], NumberStyles.None, CultureInfo.InvariantCulture, out seqid) || seqid != sizes.Count + 1) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an unexpected sequence-id token to the LIST command: {0}", tokens[0]); - continue; - } - - if (!int.TryParse (tokens[1], NumberStyles.None, CultureInfo.InvariantCulture, out size) || size < 0) { - pc.Exception = CreatePop3ParseException ("Pop3 server returned an unexpected size token to the LIST command: {0}", tokens[1]); - continue; - } - - sizes.Add (size); - } while (true); - } - - public async Task> GetSizesAsync (bool doAsync, CancellationToken cancellationToken) - { - var pc = Engine.QueueCommand (cancellationToken, OnDataReceived, "LIST"); - int id; - - do { - if (doAsync) - id = await Engine.IterateAsync ().ConfigureAwait (false); - else - id = Engine.Iterate (); - } while (id < pc.Id); - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - - if (pc.Exception != null) - throw pc.Exception; - - return sizes; - } - } - - Task> GetMessageSizesAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - var ctx = new MessageSizesContext (this); - - return ctx.GetSizesAsync (doAsync, cancellationToken); - } - - /// - /// Get the sizes for all available messages, in bytes. - /// - /// - /// Gets the sizes for all available messages, in bytes. - /// - /// The message sizes, in bytes. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IList GetMessageSizes (CancellationToken cancellationToken = default (CancellationToken)) - { - return GetMessageSizesAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - abstract class DownloadContext - { - readonly ITransferProgress progress; - readonly Pop3Client client; - T[] downloaded; - long nread; - int index; - - protected DownloadContext (Pop3Client client, ITransferProgress progress) - { - this.progress = progress; - this.client = client; - } - - protected Pop3Engine Engine { - get { return client.engine; } - } - - protected abstract T Parse (Pop3Stream data, CancellationToken cancellationToken); - - protected abstract Task ParseAsync (Pop3Stream data, CancellationToken cancellationToken); - - protected void Update (int n) - { - if (progress == null) - return; - - nread += n; - - progress.Report (nread); - } - - async Task OnDataReceived (Pop3Engine pop3, Pop3Command pc, string text, bool doAsync) - { - if (pc.Status != Pop3CommandStatus.Ok) - return; - - try { - T item; - - pop3.Stream.Mode = Pop3StreamMode.Data; - - if (doAsync) - item = await ParseAsync (pop3.Stream, pc.CancellationToken).ConfigureAwait (false); - else - item = Parse (pop3.Stream, pc.CancellationToken); - - downloaded[index++] = item; - } catch (FormatException ex) { - pc.Exception = CreatePop3ParseException (ex, "Failed to parse data."); - - if (doAsync) - await pop3.Stream.CopyToAsync (Stream.Null, 4096, pc.CancellationToken).ConfigureAwait (false); - else - pop3.Stream.CopyTo (Stream.Null, 4096); - } finally { - pop3.Stream.Mode = Pop3StreamMode.Line; - } - } - - Pop3Command QueueCommand (int seqid, bool headersOnly, CancellationToken cancellationToken) - { - if (headersOnly) - return Engine.QueueCommand (cancellationToken, OnDataReceived, "TOP {0} 0", seqid.ToString (CultureInfo.InvariantCulture)); - - return Engine.QueueCommand (cancellationToken, OnDataReceived, "RETR {0}", seqid.ToString (CultureInfo.InvariantCulture)); - } - - async Task DownloadItemAsync (int seqid, bool headersOnly, bool doAsync, CancellationToken cancellationToken) - { - var pc = QueueCommand (seqid, headersOnly, cancellationToken); - int id; - - do { - if (doAsync) - id = await Engine.IterateAsync ().ConfigureAwait (false); - else - id = Engine.Iterate (); - } while (id < pc.Id); - - if (pc.Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (pc); - - if (pc.Exception != null) - throw pc.Exception; - } - - public async Task DownloadAsync (int seqid, bool headersOnly, bool doAsync, CancellationToken cancellationToken) - { - downloaded = new T[1]; - index = 0; - - await DownloadItemAsync (seqid, headersOnly, doAsync, cancellationToken).ConfigureAwait (false); - - return downloaded[0]; - } - - public async Task> DownloadAsync (IList seqids, bool headersOnly, bool doAsync, CancellationToken cancellationToken) - { - downloaded = new T[seqids.Count]; - index = 0; - - if ((Engine.Capabilities & Pop3Capabilities.Pipelining) == 0) { - for (int i = 0; i < seqids.Count; i++) - await DownloadItemAsync (seqids[i], headersOnly, doAsync, cancellationToken); - - return downloaded; - } - - var commands = new Pop3Command[seqids.Count]; - Pop3Command pc = null; - int id; - - for (int i = 0; i < seqids.Count; i++) - commands[i] = QueueCommand (seqids[i], headersOnly, cancellationToken); - - pc = commands[commands.Length - 1]; - - do { - if (doAsync) - id = await Engine.IterateAsync ().ConfigureAwait (false); - else - id = Engine.Iterate (); - } while (id < pc.Id); - - for (int i = 0; i < commands.Length; i++) { - if (commands[i].Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (commands[i]); - - if (commands[i].Exception != null) - throw commands[i].Exception; - } - - return downloaded; - } - } - - class DownloadStreamContext : DownloadContext - { - public DownloadStreamContext (Pop3Client client, ITransferProgress progress = null) : base (client, progress) - { - } - - protected override Stream Parse (Pop3Stream data, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested (); - - var stream = new MemoryBlockStream (); - var buffer = new byte[4096]; - int nread; - - while ((nread = data.Read (buffer, 0, buffer.Length, cancellationToken)) > 0) { - stream.Write (buffer, 0, nread); - Update (nread); - } - - stream.Position = 0; - - return stream; - } - - protected override async Task ParseAsync (Pop3Stream data, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested (); - - var stream = new MemoryBlockStream (); - var buffer = new byte[4096]; - int nread; - - while ((nread = await data.ReadAsync (buffer, 0, buffer.Length, cancellationToken).ConfigureAwait (false)) > 0) { - stream.Write (buffer, 0, nread); - Update (nread); - } - - stream.Position = 0; - - return stream; - } - } - - class DownloadHeaderContext : DownloadContext - { - readonly MimeParser parser; - - public DownloadHeaderContext (Pop3Client client, MimeParser parser) : base (client, null) - { - this.parser = parser; - } - - protected override HeaderList Parse (Pop3Stream data, CancellationToken cancellationToken) - { - using (var stream = new ProgressStream (data, Update)) { - parser.SetStream (ParserOptions.Default, stream); - - return parser.ParseMessage (cancellationToken).Headers; - } - } - - protected override async Task ParseAsync (Pop3Stream data, CancellationToken cancellationToken) - { - using (var stream = new ProgressStream (data, Update)) { - parser.SetStream (ParserOptions.Default, stream); - - return (await parser.ParseMessageAsync (cancellationToken).ConfigureAwait (false)).Headers; - } - } - } - - class DownloadMessageContext : DownloadContext - { - readonly MimeParser parser; - - public DownloadMessageContext (Pop3Client client, MimeParser parser, ITransferProgress progress = null) : base (client, progress) - { - this.parser = parser; - } - - protected override MimeMessage Parse (Pop3Stream data, CancellationToken cancellationToken) - { - using (var stream = new ProgressStream (data, Update)) { - parser.SetStream (ParserOptions.Default, stream); - - return parser.ParseMessage (cancellationToken); - } - } - - protected override Task ParseAsync (Pop3Stream data, CancellationToken cancellationToken) - { - using (var stream = new ProgressStream (data, Update)) { - parser.SetStream (ParserOptions.Default, stream); - - return parser.ParseMessageAsync (cancellationToken); - } - } - } - - /// - /// Get the headers for the message at the specified index. - /// - /// - /// Gets the headers for the message at the specified index. - /// - /// The message headers. - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override HeaderList GetMessageHeaders (int index, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - var ctx = new DownloadHeaderContext (this, parser); - - return ctx.DownloadAsync (index + 1, true, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the headers for the messages at the specified indexes. - /// - /// - /// Gets the headers for the messages at the specified indexes. - /// When the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message because - /// it will batch the commands to reduce latency. - /// - /// The headers for the specified messages. - /// The indexes of the messages. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IList GetMessageHeaders (IList indexes, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (indexes.Count == 0) - return new HeaderList[0]; - - var seqids = new int[indexes.Count]; - - for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] < 0 || indexes[i] >= total) - throw new ArgumentException ("One or more of the indexes are invalid.", nameof (indexes)); - - seqids[i] = indexes[i] + 1; - } - - var ctx = new DownloadHeaderContext (this, parser); - - return ctx.DownloadAsync (seqids, true, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the headers of the messages within the specified range. - /// - /// - /// Gets the headers of the messages within the specified range. - /// When the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message because - /// it will batch the commands to reduce latency. - /// - /// The headers of the messages within the specified range. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IList GetMessageHeaders (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (startIndex < 0 || startIndex >= total) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (count < 0 || count > (total - startIndex)) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (count == 0) - return new HeaderList[0]; - - var seqids = new int[count]; - - for (int i = 0; i < count; i++) - seqids[i] = startIndex + i + 1; - - var ctx = new DownloadHeaderContext (this, parser); - - return ctx.DownloadAsync (seqids, true, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the message at the specified index. - /// - /// - /// Gets the message at the specified index. - /// - /// - /// - /// - /// The message. - /// The index of the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override MimeMessage GetMessage (int index, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - var ctx = new DownloadMessageContext (this, parser, progress); - - return ctx.DownloadAsync (index + 1, false, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the messages at the specified indexes. - /// - /// - /// Gets the messages at the specified indexes. - /// When the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message - /// because it will batch the commands to reduce latency. - /// - /// The messages. - /// The indexes of the messages. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IList GetMessages (IList indexes, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (indexes.Count == 0) - return new MimeMessage[0]; - - var seqids = new int[indexes.Count]; - - for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] < 0 || indexes[i] >= total) - throw new ArgumentException ("One or more of the indexes are invalid.", nameof (indexes)); - - seqids[i] = indexes[i] + 1; - } - - var ctx = new DownloadMessageContext (this, parser, progress); - - return ctx.DownloadAsync (seqids, false, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the messages within the specified range. - /// - /// - /// Gets the messages within the specified range. - /// When the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message - /// because it will batch the commands to reduce latency. - /// - /// - /// - /// - /// The messages. - /// The index of the first message to get. - /// The number of messages to get. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IList GetMessages (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (startIndex < 0 || startIndex >= total) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (count < 0 || count > (total - startIndex)) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (count == 0) - return new MimeMessage[0]; - - var seqids = new int[count]; - - for (int i = 0; i < count; i++) - seqids[i] = startIndex + i + 1; - - var ctx = new DownloadMessageContext (this, parser, progress); - - return ctx.DownloadAsync (seqids, false, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the message or header stream at the specified index. - /// - /// - /// Gets the message or header stream at the specified index. - /// - /// The message or header stream. - /// The index of the message. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override Stream GetStream (int index, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - var ctx = new DownloadStreamContext (this, progress); - - return ctx.DownloadAsync (index + 1, headersOnly, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the message or header streams at the specified indexes. - /// - /// - /// Get the message or header streams at the specified indexes. - /// If the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message - /// because it will batch the commands to reduce latency. - /// - /// The message or header streams. - /// The indexes of the messages. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IList GetStreams (IList indexes, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (indexes.Count == 0) - return new Stream[0]; - - var seqids = new int[indexes.Count]; - - for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] < 0 || indexes[i] >= total) - throw new ArgumentException ("One or more of the indexes are invalid.", nameof (indexes)); - - seqids[i] = indexes[i] + 1; - } - - var ctx = new DownloadStreamContext (this, progress); - - return ctx.DownloadAsync (seqids, headersOnly, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Get the message or header streams within the specified range. - /// - /// - /// Gets the message or header streams within the specified range. - /// If the POP3 server supports the - /// extension, this method will likely be more efficient than using - /// for each message - /// because it will batch the commands to reduce latency. - /// - /// The message or header streams. - /// The index of the first stream to get. - /// The number of streams to get. - /// true if only the headers should be retrieved; otherwise, false. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The POP3 server does not support the UIDL extension. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IList GetStreams (int startIndex, int count, bool headersOnly = false, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (startIndex < 0 || startIndex >= total) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (count < 0 || count > (total - startIndex)) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (count == 0) - return new Stream[0]; - - var seqids = new int[count]; - - for (int i = 0; i < count; i++) - seqids[i] = startIndex + i + 1; - - var ctx = new DownloadStreamContext (this, progress); - - return ctx.DownloadAsync (seqids, headersOnly, false, cancellationToken).GetAwaiter ().GetResult (); - } - - Task DeleteMessageAsync (int index, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (index < 0 || index >= total) - throw new ArgumentOutOfRangeException (nameof (index)); - - var seqid = (index + 1).ToString (CultureInfo.InvariantCulture); - - return SendCommandAsync (doAsync, cancellationToken, "DELE {0}", seqid); - } - - /// - /// Mark the specified message for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// - /// - /// - /// The index of the message. - /// The cancellation token. - /// - /// is not a valid message index. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void DeleteMessage (int index, CancellationToken cancellationToken = default (CancellationToken)) - { - DeleteMessageAsync (index, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task DeleteMessagesAsync (IList indexes, bool doAsync, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (indexes == null) - throw new ArgumentNullException (nameof (indexes)); - - if (indexes.Count == 0) - return; - - var seqids = new string[indexes.Count]; - - for (int i = 0; i < indexes.Count; i++) { - if (indexes[i] < 0 || indexes[i] >= total) - throw new ArgumentException ("One or more of the indexes are invalid.", nameof (indexes)); - - seqids[i] = (indexes[i] + 1).ToString (CultureInfo.InvariantCulture); - } - - if ((Capabilities & Pop3Capabilities.Pipelining) == 0) { - for (int i = 0; i < seqids.Length; i++) - await SendCommandAsync (doAsync, cancellationToken, "DELE {0}", seqids[i]).ConfigureAwait (false); - - return; - } - - var commands = new Pop3Command[seqids.Length]; - Pop3Command pc = null; - int id; - - for (int i = 0; i < seqids.Length; i++) { - pc = engine.QueueCommand (cancellationToken, null, "DELE {0}", seqids[i]); - commands[i] = pc; - } - - do { - if (doAsync) - id = await engine.IterateAsync ().ConfigureAwait (false); - else - id = engine.Iterate (); - } while (id < pc.Id); - - for (int i = 0; i < commands.Length; i++) { - if (commands[i].Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (commands[i]); - } - } - - /// - /// Mark the specified messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The indexes of the messages. - /// The cancellation token. - /// - /// is null. - /// - /// - /// One or more of the are invalid. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void DeleteMessages (IList indexes, CancellationToken cancellationToken = default (CancellationToken)) - { - DeleteMessagesAsync (indexes, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task DeleteMessagesAsync (int startIndex, int count, bool doAsync, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - if (startIndex < 0 || startIndex >= total) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (count < 0 || count > (total - startIndex)) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (count == 0) - return; - - if ((Capabilities & Pop3Capabilities.Pipelining) == 0) { - for (int i = 0; i < count; i++) { - var seqid = (startIndex + i + 1).ToString (CultureInfo.InvariantCulture); - await SendCommandAsync (doAsync, cancellationToken, "DELE {0}", seqid).ConfigureAwait (false); - } - - return; - } - - var commands = new Pop3Command[count]; - Pop3Command pc = null; - int id; - - for (int i = 0; i < count; i++) { - var seqid = (startIndex + i + 1).ToString (CultureInfo.InvariantCulture); - pc = engine.QueueCommand (cancellationToken, null, "DELE {0}", seqid); - commands[i] = pc; - } - - do { - if (doAsync) - id = await engine.IterateAsync ().ConfigureAwait (false); - else - id = engine.Iterate (); - } while (id < pc.Id); - - for (int i = 0; i < commands.Length; i++) { - if (commands[i].Status != Pop3CommandStatus.Ok) - throw CreatePop3Exception (commands[i]); - } - } - - /// - /// Mark the specified range of messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// - /// - /// - /// The index of the first message to mark for deletion. - /// The number of messages to mark for deletion. - /// The cancellation token. - /// - /// and do not specify - /// a valid range of messages. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void DeleteMessages (int startIndex, int count, CancellationToken cancellationToken = default (CancellationToken)) - { - DeleteMessagesAsync (startIndex, count, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Mark all messages for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void DeleteAllMessages (CancellationToken cancellationToken = default (CancellationToken)) - { - if (total > 0) - DeleteMessages (0, total, cancellationToken); - } - - Task ResetAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - return SendCommandAsync (doAsync, cancellationToken, "RSET"); - } - - /// - /// Reset the state of all messages marked for deletion. - /// - /// - /// Messages marked for deletion are not actually deleted until the session - /// is cleanly disconnected - /// (see ). - /// - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// The POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override void Reset (CancellationToken cancellationToken = default (CancellationToken)) - { - ResetAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the messages in the folder. - /// - /// - /// Gets an enumerator for the messages in the folder. - /// - /// The enumerator. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The is not authenticated. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// A POP3 command failed. - /// - /// - /// A POP3 protocol error occurred. - /// - public override IEnumerator GetEnumerator () - { - CheckDisposed (); - CheckConnected (); - CheckAuthenticated (); - - for (int i = 0; i < total; i++) - yield return GetMessage (i, CancellationToken.None); - - yield break; - } - - #endregion - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - engine.Disconnect (); - disposed = true; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MailKit/Net/Pop3/Pop3Command.cs b/src/MailKit/Net/Pop3/Pop3Command.cs deleted file mode 100644 index 9b43ec9..0000000 --- a/src/MailKit/Net/Pop3/Pop3Command.cs +++ /dev/null @@ -1,74 +0,0 @@ -// -// Pop3Command.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; - -namespace MailKit.Net.Pop3 { - /// - /// POP3 command handler. - /// - /// - /// All exceptions thrown by the handler are considered fatal and will - /// force-disconnect the connection. If a non-fatal error occurs, set - /// it on the property. - /// - delegate Task Pop3CommandHandler (Pop3Engine engine, Pop3Command pc, string text, bool doAsync); - - enum Pop3CommandStatus { - Queued = -5, - Active = -4, - Continue = -3, - ProtocolError = -2, - Error = -1, - Ok = 0 - } - - class Pop3Command - { - public CancellationToken CancellationToken { get; private set; } - public Pop3CommandHandler Handler { get; private set; } - public Encoding Encoding { get; private set; } - public string Command { get; private set; } - public int Id { get; internal set; } - - // output - public Pop3CommandStatus Status { get; internal set; } - public ProtocolException Exception { get; set; } - public string StatusText { get; set; } - - public Pop3Command (CancellationToken cancellationToken, Pop3CommandHandler handler, Encoding encoding, string format, params object[] args) - { - Command = string.Format (CultureInfo.InvariantCulture, format, args); - CancellationToken = cancellationToken; - Encoding = encoding; - Handler = handler; - } - } -} diff --git a/src/MailKit/Net/Pop3/Pop3CommandException.cs b/src/MailKit/Net/Pop3/Pop3CommandException.cs deleted file mode 100644 index eee7b4f..0000000 --- a/src/MailKit/Net/Pop3/Pop3CommandException.cs +++ /dev/null @@ -1,179 +0,0 @@ -// -// Pop3CommandException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit.Net.Pop3 { - /// - /// A POP3 command exception. - /// - /// - /// The exception that is thrown when a POP3 command fails. Unlike a , - /// a does not require the to be reconnected. - /// - /// - /// - /// -#if SERIALIZABLE - [Serializable] -#endif - public class Pop3CommandException : CommandException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the serialized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected Pop3CommandException (SerializationInfo info, StreamingContext context) : base (info, context) - { - StatusText = info.GetString ("StatusText"); - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// An inner exception. - public Pop3CommandException (string message, Exception innerException) : base (message, innerException) - { - StatusText = string.Empty; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The response status text. - /// An inner exception. - /// - /// is null. - /// - public Pop3CommandException (string message, string statusText, Exception innerException) : base (message, innerException) - { - if (statusText == null) - throw new ArgumentNullException (nameof (statusText)); - - StatusText = statusText; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public Pop3CommandException (string message) : base (message) - { - StatusText = string.Empty; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The response status text. - /// - /// is null. - /// - public Pop3CommandException (string message, string statusText) : base (message) - { - if (statusText == null) - throw new ArgumentNullException (nameof (statusText)); - - StatusText = statusText; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public Pop3CommandException () - { - StatusText = string.Empty; - } - - /// - /// Get the response status text. - /// - /// - /// Gets the response status text. - /// - /// - /// - /// - /// The response status text. - public string StatusText { - get; private set; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Serializes the state of the . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("StatusText", StatusText); - } -#endif - } -} diff --git a/src/MailKit/Net/Pop3/Pop3Engine.cs b/src/MailKit/Net/Pop3/Pop3Engine.cs deleted file mode 100644 index 8b3d3b9..0000000 --- a/src/MailKit/Net/Pop3/Pop3Engine.cs +++ /dev/null @@ -1,655 +0,0 @@ -// -// Pop3Engine.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MailKit.Net.Pop3 { - /// - /// The state of the . - /// - enum Pop3EngineState { - /// - /// The Pop3Engine is in the disconnected state. - /// - Disconnected, - - /// - /// The Pop3Engine is in the connected state. - /// - Connected, - - /// - /// The Pop3Engine is in the transaction state, indicating that it is - /// authenticated and may retrieve messages from the server. - /// - Transaction - } - - /// - /// A POP3 command engine. - /// - class Pop3Engine - { - static readonly Encoding Latin1; - static readonly Encoding UTF8; - - readonly List queue; - Pop3Stream stream; - int nextId; - - static Pop3Engine () - { - UTF8 = Encoding.GetEncoding (65001, new EncoderExceptionFallback (), new DecoderExceptionFallback ()); - - try { - Latin1 = Encoding.GetEncoding (28591); - } catch (NotSupportedException) { - Latin1 = Encoding.GetEncoding (1252); - } - } - - /// - /// Initializes a new instance of the class. - /// - public Pop3Engine () - { - AuthenticationMechanisms = new HashSet (StringComparer.Ordinal); - Capabilities = Pop3Capabilities.User; - queue = new List (); - nextId = 1; - } - - /// - /// Gets the URI of the POP3 server. - /// - /// - /// Gets the URI of the POP3 server. - /// - /// The URI of the POP3 server. - public Uri Uri { - get; internal set; - } - - /// - /// Gets the authentication mechanisms supported by the POP3 server. - /// - /// - /// The authentication mechanisms are queried durring the - /// method. - /// - /// The authentication mechanisms. - public HashSet AuthenticationMechanisms { - get; private set; - } - - /// - /// Gets the capabilities supported by the POP3 server. - /// - /// - /// The capabilities will not be known until a successful connection - /// has been made via the method. - /// - /// The capabilities. - public Pop3Capabilities Capabilities { - get; set; - } - - /// - /// Gets the underlying POP3 stream. - /// - /// - /// Gets the underlying POP3 stream. - /// - /// The pop3 stream. - public Pop3Stream Stream { - get { return stream; } - } - - /// - /// Gets or sets the state of the engine. - /// - /// - /// Gets or sets the state of the engine. - /// - /// The engine state. - public Pop3EngineState State { - get; internal set; - } - - /// - /// Gets whether or not the engine is currently connected to a POP3 server. - /// - /// - /// Gets whether or not the engine is currently connected to a POP3 server. - /// - /// true if the engine is connected; otherwise, false. - public bool IsConnected { - get { return stream != null && stream.IsConnected; } - } - - /// - /// Gets the APOP authentication token. - /// - /// - /// Gets the APOP authentication token. - /// - /// The APOP authentication token. - public string ApopToken { - get; private set; - } - - /// - /// Gets the EXPIRE extension policy value. - /// - /// - /// Gets the EXPIRE extension policy value. - /// - /// The EXPIRE policy. - public int ExpirePolicy { - get; private set; - } - - /// - /// Gets the implementation details of the server. - /// - /// - /// Gets the implementation details of the server. - /// - /// The implementation details. - public string Implementation { - get; private set; - } - - /// - /// Gets the login delay. - /// - /// - /// Gets the login delay. - /// - /// The login delay. - public int LoginDelay { - get; private set; - } - - async Task ConnectAsync (Pop3Stream pop3, bool doAsync, CancellationToken cancellationToken) - { - if (stream != null) - stream.Dispose (); - - Capabilities = Pop3Capabilities.User; - AuthenticationMechanisms.Clear (); - State = Pop3EngineState.Disconnected; - ApopToken = null; - stream = pop3; - - // read the pop3 server greeting - var greeting = (await ReadLineAsync (doAsync, cancellationToken).ConfigureAwait (false)).TrimEnd (); - - int index = greeting.IndexOf (' '); - string token, text; - - if (index != -1) { - token = greeting.Substring (0, index); - - while (index < greeting.Length && char.IsWhiteSpace (greeting[index])) - index++; - - if (index < greeting.Length) - text = greeting.Substring (index); - else - text = string.Empty; - } else { - text = string.Empty; - token = greeting; - } - - if (token != "+OK") { - stream.Dispose (); - stream = null; - - throw new Pop3ProtocolException (string.Format ("Unexpected greeting from server: {0}", greeting)); - } - - index = text.IndexOf ('<'); - if (index != -1 && index + 1 < text.Length) { - int endIndex = text.IndexOf ('>', index + 1); - - if (endIndex++ != -1) { - ApopToken = text.Substring (index, endIndex - index); - Capabilities |= Pop3Capabilities.Apop; - } - } - - State = Pop3EngineState.Connected; - } - - /// - /// Takes posession of the and reads the greeting. - /// - /// - /// Takes posession of the and reads the greeting. - /// - /// The pop3 stream. - /// The cancellation token - public void Connect (Pop3Stream pop3, CancellationToken cancellationToken) - { - ConnectAsync (pop3, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Takes posession of the and reads the greeting. - /// - /// - /// Takes posession of the and reads the greeting. - /// - /// The pop3 stream. - /// The cancellation token - public Task ConnectAsync (Pop3Stream pop3, CancellationToken cancellationToken) - { - return ConnectAsync (pop3, true, cancellationToken); - } - - public event EventHandler Disconnected; - - void OnDisconnected () - { - var handler = Disconnected; - - if (handler != null) - handler (this, EventArgs.Empty); - } - - /// - /// Disconnects the . - /// - /// - /// Disconnects the . - /// - public void Disconnect () - { - if (stream != null) { - stream.Dispose (); - stream = null; - } - - if (State != Pop3EngineState.Disconnected) { - State = Pop3EngineState.Disconnected; - OnDisconnected (); - } - } - - async Task ReadLineAsync (bool doAsync, CancellationToken cancellationToken) - { - if (stream == null) - throw new InvalidOperationException (); - - using (var memory = new MemoryStream ()) { - bool complete; - byte[] buf; - int count; - - do { - if (doAsync) - complete = await stream.ReadLineAsync (memory, cancellationToken).ConfigureAwait (false); - else - complete = stream.ReadLine (memory, cancellationToken); - } while (!complete); - - count = (int) memory.Length; -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - buf = memory.GetBuffer (); -#else - buf = memory.ToArray (); -#endif - - // Trim the sequence from the end of the line. - if (buf[count - 1] == (byte) '\n') { - count--; - - if (buf[count - 1] == (byte) '\r') - count--; - } - - try { - return UTF8.GetString (buf, 0, count); - } catch (DecoderFallbackException) { - return Latin1.GetString (buf, 0, count); - } - } - } - - /// - /// Reads a single line from the . - /// - /// The line. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public string ReadLine (CancellationToken cancellationToken) - { - return ReadLineAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Reads a single line from the . - /// - /// The line. - /// The cancellation token. - /// - /// The engine is not connected. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task ReadLineAsync (CancellationToken cancellationToken) - { - return ReadLineAsync (true, cancellationToken); - } - - public static Pop3CommandStatus GetCommandStatus (string response, out string text) - { - int index = response.IndexOf (' '); - string token; - - if (index != -1) { - token = response.Substring (0, index); - - while (index < response.Length && char.IsWhiteSpace (response[index])) - index++; - - if (index < response.Length) - text = response.Substring (index); - else - text = string.Empty; - } else { - text = string.Empty; - token = response; - } - - if (token == "+OK") - return Pop3CommandStatus.Ok; - - if (token == "-ERR") - return Pop3CommandStatus.Error; - - if (token == "+") - return Pop3CommandStatus.Continue; - - return Pop3CommandStatus.ProtocolError; - } - - async Task SendCommandAsync (Pop3Command pc, bool doAsync, CancellationToken cancellationToken) - { - var buf = pc.Encoding.GetBytes (pc.Command + "\r\n"); - - if (doAsync) - await stream.WriteAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false); - else - stream.Write (buf, 0, buf.Length, cancellationToken); - } - - async Task ReadResponseAsync (Pop3Command pc, bool doAsync) - { - string response, text; - - try { - response = (await ReadLineAsync (doAsync, pc.CancellationToken).ConfigureAwait (false)).TrimEnd (); - } catch { - pc.Status = Pop3CommandStatus.ProtocolError; - Disconnect (); - throw; - } - - pc.Status = GetCommandStatus (response, out text); - pc.StatusText = text; - - switch (pc.Status) { - case Pop3CommandStatus.ProtocolError: - Disconnect (); - throw new Pop3ProtocolException (string.Format ("Unexpected response from server: {0}", response)); - case Pop3CommandStatus.Continue: - case Pop3CommandStatus.Ok: - if (pc.Handler != null) { - try { - await pc.Handler (this, pc, text, doAsync).ConfigureAwait (false); - } catch { - pc.Status = Pop3CommandStatus.ProtocolError; - Disconnect (); - throw; - } - } - break; - } - } - - async Task IterateAsync (bool doAsync) - { - if (stream == null) - throw new InvalidOperationException (); - - if (queue.Count == 0) - return 0; - - int count = (Capabilities & Pop3Capabilities.Pipelining) != 0 ? queue.Count : 1; - var cancellationToken = queue[0].CancellationToken; - var active = new List (); - - if (cancellationToken.IsCancellationRequested) { - queue.RemoveAll (x => x.CancellationToken.IsCancellationRequested); - cancellationToken.ThrowIfCancellationRequested (); - } - - for (int i = 0; i < count; i++) { - var pc = queue[0]; - - if (i > 0 && !pc.CancellationToken.Equals (cancellationToken)) - break; - - queue.RemoveAt (0); - - pc.Status = Pop3CommandStatus.Active; - active.Add (pc); - - await SendCommandAsync (pc, doAsync, cancellationToken).ConfigureAwait (false); - } - - if (doAsync) - await stream.FlushAsync (cancellationToken).ConfigureAwait (false); - else - stream.Flush (cancellationToken); - - for (int i = 0; i < active.Count; i++) - await ReadResponseAsync (active[i], doAsync).ConfigureAwait (false); - - return active[active.Count - 1].Id; - } - - /// - /// Iterate the command pipeline. - /// - /// The ID of the command that just completed. - public int Iterate () - { - return IterateAsync (false).GetAwaiter ().GetResult (); - } - - /// - /// Iterate the command pipeline. - /// - /// The ID of the command that just completed. - public Task IterateAsync () - { - return IterateAsync (true); - } - - public Pop3Command QueueCommand (CancellationToken cancellationToken, Pop3CommandHandler handler, Encoding encoding, string format, params object[] args) - { - var pc = new Pop3Command (cancellationToken, handler, encoding, format, args); - pc.Id = nextId++; - queue.Add (pc); - return pc; - } - - public Pop3Command QueueCommand (CancellationToken cancellationToken, Pop3CommandHandler handler, string format, params object[] args) - { - return QueueCommand (cancellationToken, handler, Encoding.ASCII, format, args); - } - - static async Task CapaHandler (Pop3Engine engine, Pop3Command pc, string text, bool doAsync) - { - if (pc.Status != Pop3CommandStatus.Ok) - return; - - string response; - - do { - if ((response = await engine.ReadLineAsync (doAsync, pc.CancellationToken).ConfigureAwait (false)) == ".") - break; - - int index = response.IndexOf (' '); - string token, data; - int value; - - if (index != -1) { - token = response.Substring (0, index); - - while (index < response.Length && char.IsWhiteSpace (response[index])) - index++; - - if (index < response.Length) - data = response.Substring (index); - else - data = string.Empty; - } else { - data = string.Empty; - token = response; - } - - switch (token) { - case "EXPIRE": - engine.Capabilities |= Pop3Capabilities.Expire; - var tokens = data.Split (' '); - - if (int.TryParse (tokens[0], NumberStyles.None, CultureInfo.InvariantCulture, out value)) - engine.ExpirePolicy = value; - else if (tokens[0] == "NEVER") - engine.ExpirePolicy = -1; - break; - case "IMPLEMENTATION": - engine.Implementation = data; - break; - case "LOGIN-DELAY": - if (int.TryParse (data, NumberStyles.None, CultureInfo.InvariantCulture, out value)) { - engine.Capabilities |= Pop3Capabilities.LoginDelay; - engine.LoginDelay = value; - } - break; - case "PIPELINING": - engine.Capabilities |= Pop3Capabilities.Pipelining; - break; - case "RESP-CODES": - engine.Capabilities |= Pop3Capabilities.ResponseCodes; - break; - case "SASL": - engine.Capabilities |= Pop3Capabilities.Sasl; - foreach (var authmech in data.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) - engine.AuthenticationMechanisms.Add (authmech); - break; - case "STLS": - engine.Capabilities |= Pop3Capabilities.StartTLS; - break; - case "TOP": - engine.Capabilities |= Pop3Capabilities.Top; - break; - case "UIDL": - engine.Capabilities |= Pop3Capabilities.UIDL; - break; - case "USER": - engine.Capabilities |= Pop3Capabilities.User; - break; - case "UTF8": - engine.Capabilities |= Pop3Capabilities.UTF8; - - foreach (var item in data.Split (' ')) { - if (item == "USER") - engine.Capabilities |= Pop3Capabilities.UTF8User; - } - break; - case "LANG": - engine.Capabilities |= Pop3Capabilities.Lang; - break; - } - } while (true); - } - - async Task QueryCapabilitiesAsync (bool doAsync, CancellationToken cancellationToken) - { - if (stream == null) - throw new InvalidOperationException (); - - // Clear all CAPA response capabilities (except the APOP, USER, and STLS capabilities). - Capabilities &= Pop3Capabilities.Apop | Pop3Capabilities.User | Pop3Capabilities.StartTLS; - AuthenticationMechanisms.Clear (); - Implementation = null; - ExpirePolicy = 0; - LoginDelay = 0; - - var pc = QueueCommand (cancellationToken, CapaHandler, "CAPA"); - - while (await IterateAsync (doAsync).ConfigureAwait (false) < pc.Id) { - // continue processing commands... - } - - return pc.Status; - } - - public Pop3CommandStatus QueryCapabilities (CancellationToken cancellationToken) - { - return QueryCapabilitiesAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - public Task QueryCapabilitiesAsync (CancellationToken cancellationToken) - { - return QueryCapabilitiesAsync (true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Pop3/Pop3Language.cs b/src/MailKit/Net/Pop3/Pop3Language.cs deleted file mode 100644 index 4e8e559..0000000 --- a/src/MailKit/Net/Pop3/Pop3Language.cs +++ /dev/null @@ -1,71 +0,0 @@ -// -// Pop3Language.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Net.Pop3 { - /// - /// A POP3 language. - /// - /// - /// A POP3 language. - /// - public class Pop3Language - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - internal Pop3Language (string lang, string desc) - { - Language = lang; - Description = desc; - } - - /// - /// Get the language code. - /// - /// - /// Gets the language code. This is the value that should be given to - /// . - /// - /// The language. - public string Language { - get; private set; - } - - /// - /// Get the description. - /// - /// - /// Gets the description. - /// - /// The description. - public string Description { - get; private set; - } - } -} diff --git a/src/MailKit/Net/Pop3/Pop3ProtocolException.cs b/src/MailKit/Net/Pop3/Pop3ProtocolException.cs deleted file mode 100644 index bf288e5..0000000 --- a/src/MailKit/Net/Pop3/Pop3ProtocolException.cs +++ /dev/null @@ -1,101 +0,0 @@ -// -// Pop3ProtocolException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit.Net.Pop3 { - /// - /// A POP3 protocol exception. - /// - /// - /// The exception that is thrown when there is an error communicating with a POP3 server. A - /// is typically fatal and requires the - /// to be reconnected. - /// - /// - /// - /// -#if SERIALIZABLE - [Serializable] -#endif - public class Pop3ProtocolException : ProtocolException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the serialized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected Pop3ProtocolException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// An inner exception. - public Pop3ProtocolException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public Pop3ProtocolException (string message) : base (message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public Pop3ProtocolException () - { - } - } -} diff --git a/src/MailKit/Net/Pop3/Pop3Stream.cs b/src/MailKit/Net/Pop3/Pop3Stream.cs deleted file mode 100644 index 4ead936..0000000 --- a/src/MailKit/Net/Pop3/Pop3Stream.cs +++ /dev/null @@ -1,950 +0,0 @@ -// -// Pop3Stream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Net.Sockets; -using System.Threading.Tasks; - -using MimeKit.IO; - -using Buffer = System.Buffer; -using SslStream = MailKit.Net.SslStream; -using NetworkStream = MailKit.Net.NetworkStream; - -namespace MailKit.Net.Pop3 { - /// - /// An enumeration of the possible POP3 streaming modes. - /// - /// - /// Normal operation is done in the mode, - /// but when retrieving messages (via RETR) or headers (via TOP), the - /// mode should be used. - /// - enum Pop3StreamMode { - /// - /// Reads 1 line at a time. - /// - Line, - - /// - /// Reads data in chunks, ignoring line state. - /// - Data - } - - /// - /// A stream for communicating with a POP3 server. - /// - /// - /// A stream capable of reading data line-by-line () - /// or by raw byte streams (). - /// - class Pop3Stream : Stream, ICancellableStream - { - const int ReadAheadSize = 128; - const int BlockSize = 4096; - const int PadSize = 4; - - // I/O buffering - readonly byte[] input = new byte[ReadAheadSize + BlockSize + PadSize]; - const int inputStart = ReadAheadSize; - - readonly byte[] output = new byte[BlockSize]; - int outputIndex; - - readonly IProtocolLogger logger; - int inputIndex = ReadAheadSize; - int inputEnd = ReadAheadSize; - Pop3StreamMode mode; - bool disposed; - bool midline; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The underlying network stream. - /// The protocol logger. - public Pop3Stream (Stream source, IProtocolLogger protocolLogger) - { - logger = protocolLogger; - IsConnected = true; - Stream = source; - } - - /// - /// Get or sets the underlying network stream. - /// - /// - /// Gets or sets the underlying network stream. - /// - /// The underlying network stream. - public Stream Stream { - get; internal set; - } - - /// - /// Gets or sets the mode used for reading. - /// - /// The mode. - public Pop3StreamMode Mode { - get { return mode; } - set { - IsEndOfData = false; - mode = value; - } - } - - /// - /// Get whether or not the stream is connected. - /// - /// - /// Gets whether or not the stream is connected. - /// - /// true if the stream is connected; otherwise, false. - public bool IsConnected { - get; private set; - } - - /// - /// Get whether or not the end of the raw data has been reached in mode. - /// - /// - /// When reading the resonse to a command such as RETR, the end of the data is marked by line matching ".\r\n". - /// - /// true if the end of the data has been reached; otherwise, false. - public bool IsEndOfData { - get; private set; - } - - /// - /// Get whether the stream supports reading. - /// - /// - /// Gets whether the stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return Stream.CanRead; } - } - - /// - /// Get whether the stream supports writing. - /// - /// - /// Gets whether the stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return Stream.CanWrite; } - } - - /// - /// Get whether the stream supports seeking. - /// - /// - /// Gets whether the stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return false; } - } - - /// - /// Get whether the stream supports I/O timeouts. - /// - /// - /// Gets whether the stream supports I/O timeouts. - /// - /// true if the stream supports I/O timeouts; otherwise, false. - public override bool CanTimeout { - get { return Stream.CanTimeout; } - } - - /// - /// Get or set a value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// - /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// A value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// The read timeout. - public override int ReadTimeout { - get { return Stream.ReadTimeout; } - set { Stream.ReadTimeout = value; } - } - - /// - /// Get or set a value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// - /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// A value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// The write timeout. - public override int WriteTimeout { - get { return Stream.WriteTimeout; } - set { Stream.WriteTimeout = value; } - } - - /// - /// Get or set the position within the current stream. - /// - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return Stream.Position; } - set { throw new NotSupportedException (); } - } - - /// - /// Get the length of the stream, in bytes. - /// - /// - /// Gets the length of the stream, in bytes. - /// - /// A long value representing the length of the stream in bytes. - /// The length of the stream. - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Length { - get { return Stream.Length; } - } - - async Task ReadAheadAsync (bool doAsync, CancellationToken cancellationToken) - { - int left = inputEnd - inputIndex; - int start = inputStart; - int end = inputEnd; - int nread; - - if (left > 0) { - int index = inputIndex; - - // attempt to align the end of the remaining input with ReadAheadSize - if (index >= start) { - start -= Math.Min (ReadAheadSize, left); - Buffer.BlockCopy (input, index, input, start, left); - index = start; - start += left; - } else if (index > 0) { - int shift = Math.Min (index, end - start); - Buffer.BlockCopy (input, index, input, index - shift, left); - index -= shift; - start = index + left; - } else { - // we can't shift... - start = end; - } - - inputIndex = index; - inputEnd = start; - } else { - inputIndex = start; - inputEnd = start; - } - - end = input.Length - PadSize; - - try { - var network = Stream as NetworkStream; - - cancellationToken.ThrowIfCancellationRequested (); - - if (doAsync) { - nread = await Stream.ReadAsync (input, start, end - start, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectRead, cancellationToken); - nread = Stream.Read (input, start, end - start); - } - - if (nread > 0) { - logger.LogServer (input, start, nread); - inputEnd += nread; - } else { - throw new Pop3ProtocolException ("The POP3 server has unexpectedly disconnected."); - } - } catch { - IsConnected = false; - throw; - } - - return inputEnd - inputIndex; - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (Pop3Stream)); - } - - bool NeedInput (int index, int inputLeft) - { - if (inputLeft == 2 && input[index] == (byte) '.' && input[index + 1] == '\n') - return false; - - return true; - } - - async Task ReadAsync (byte[] buffer, int offset, int count, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - if (Mode != Pop3StreamMode.Data) - throw new InvalidOperationException (); - - if (IsEndOfData || count == 0) - return 0; - - int end = offset + count; - int index = offset; - int inputLeft; - - do { - inputLeft = inputEnd - inputIndex; - - // we need at least 3 bytes: ".\r\n" - if (inputLeft < 3 && (midline || NeedInput (inputIndex, inputLeft))) { - if (index > offset) - break; - - await ReadAheadAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - // terminate the input buffer with a '\n' to remove bounds checking in our inner loop - input[inputEnd] = (byte) '\n'; - - while (inputIndex < inputEnd) { - if (midline) { - // read until end-of-line - while (index < end && input[inputIndex] != (byte) '\n') - buffer[index++] = input[inputIndex++]; - - if (inputIndex == inputEnd || index == end) - break; - - // consume the '\n' character - buffer[index++] = input[inputIndex++]; - midline = false; - } - - if (inputIndex == inputEnd) - break; - - if (input[inputIndex] == (byte) '.') { - inputLeft = inputEnd - inputIndex; - - // check for ".\r\n" which signifies the end of the data stream - if (inputLeft >= 3 && input[inputIndex + 1] == (byte) '\r' && input[inputIndex + 2] == (byte) '\n') { - IsEndOfData = true; - midline = false; - inputIndex += 3; - break; - } - - // check for ".\n" which is used by some broken UNIX servers in place of ".\r\n" - if (inputLeft >= 2 && input[inputIndex + 1] == (byte) '\n') { - IsEndOfData = true; - midline = false; - inputIndex += 2; - break; - } - - // check for "." or ".\r" which might be an incomplete termination sequence - if (inputLeft == 1 || (inputLeft == 2 && input[inputIndex + 1] == (byte) '\r')) { - // not enough data... - break; - } - - // check for lines beginning with ".." which should be transformed into "." - if (input[inputIndex + 1] == (byte) '.') - inputIndex++; - } - - midline = true; - } - } while (index < end && !IsEndOfData); - - return index - offset; - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream is in line mode (see ). - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public int Read (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return ReadAsync (buffer, offset, count, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream is in line mode (see ). - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - return Read (buffer, offset, count, CancellationToken.None); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream is in line mode (see ). - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return ReadAsync (buffer, offset, count, true, cancellationToken); - } - - async Task ReadLineAsync (Stream ostream, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (inputIndex == inputEnd) - await ReadAheadAsync (doAsync, cancellationToken).ConfigureAwait (false); - - unsafe { - fixed (byte* inbuf = input) { - byte* start, inptr, inend; - int offset = inputIndex; - int count; - - start = inbuf + inputIndex; - inend = inbuf + inputEnd; - *inend = (byte) '\n'; - inptr = start; - - // FIXME: use SIMD to optimize this - while (*inptr != (byte) '\n') - inptr++; - - inputIndex = (int) (inptr - inbuf); - count = (int) (inptr - start); - - if (inptr == inend) { - ostream.Write (input, offset, count); - midline = true; - return false; - } - - // consume the '\n' - midline = false; - inputIndex++; - count++; - - ostream.Write (input, offset, count); - - return true; - } - } - } - - /// - /// Reads a single line of input from the stream. - /// - /// - /// This method should be called in a loop until it returns true. - /// - /// true, if reading the line is complete, false otherwise. - /// The output stream to write the line data into. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - internal bool ReadLine (Stream ostream, CancellationToken cancellationToken) - { - return ReadLineAsync (ostream, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously reads a single line of input from the stream. - /// - /// - /// This method should be called in a loop until it returns true. - /// - /// true, if reading the line is complete, false otherwise. - /// The output stream to write the line data into. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - internal Task ReadLineAsync (Stream ostream, CancellationToken cancellationToken) - { - return ReadLineAsync (ostream, true, cancellationToken); - } - - async Task WriteAsync (byte[] buffer, int offset, int count, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - try { - var network = NetworkStream.Get (Stream); - int index = offset; - int left = count; - - while (left > 0) { - int n = Math.Min (BlockSize - outputIndex, left); - - if (outputIndex > 0 || n < BlockSize) { - // append the data to the output buffer - Buffer.BlockCopy (buffer, index, output, outputIndex, n); - outputIndex += n; - index += n; - left -= n; - } - - if (outputIndex == BlockSize) { - // flush the output buffer - if (doAsync) { - await Stream.WriteAsync (output, 0, BlockSize, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (output, 0, BlockSize); - } - logger.LogClient (output, 0, BlockSize); - outputIndex = 0; - } - - if (outputIndex == 0) { - // write blocks of data to the stream without buffering - while (left >= BlockSize) { - if (doAsync) { - await Stream.WriteAsync (buffer, index, BlockSize, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (buffer, index, BlockSize); - } - logger.LogClient (buffer, index, BlockSize); - index += BlockSize; - left -= BlockSize; - } - } - } - } catch (Exception ex) { - IsConnected = false; - if (!(ex is OperationCanceledException)) - cancellationToken.ThrowIfCancellationRequested (); - throw; - } - - IsEndOfData = false; - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void Write (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - WriteAsync (buffer, offset, count, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - Write (buffer, offset, count, CancellationToken.None); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return WriteAsync (buffer, offset, count, true, cancellationToken); - } - - async Task FlushAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (outputIndex == 0) - return; - - try { - if (doAsync) { - await Stream.WriteAsync (output, 0, outputIndex, cancellationToken).ConfigureAwait (false); - await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - var network = NetworkStream.Get (Stream); - - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (output, 0, outputIndex); - Stream.Flush (); - } - logger.LogClient (output, 0, outputIndex); - outputIndex = 0; - } catch (Exception ex) { - IsConnected = false; - if (!(ex is OperationCanceledException)) - cancellationToken.ThrowIfCancellationRequested (); - throw; - } - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void Flush (CancellationToken cancellationToken) - { - FlushAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Flush () - { - Flush (CancellationToken.None); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task FlushAsync (CancellationToken cancellationToken) - { - return FlushAsync (true, cancellationToken); - } - - /// - /// Sets the position within the current stream. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// The stream does not support seeking. - /// - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - /// - /// Sets the length of the stream. - /// - /// The desired length of the stream in bytes. - /// - /// The stream does not support setting the length. - /// - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - IsConnected = false; - Stream.Dispose (); - } - - disposed = true; - - base.Dispose (disposing); - } - } -} diff --git a/src/MailKit/Net/Proxy/HttpProxyClient.cs b/src/MailKit/Net/Proxy/HttpProxyClient.cs deleted file mode 100644 index def93e0..0000000 --- a/src/MailKit/Net/Proxy/HttpProxyClient.cs +++ /dev/null @@ -1,248 +0,0 @@ -// -// HttpProxyClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Globalization; -using System.Threading.Tasks; - -namespace MailKit.Net.Proxy -{ - /// - /// An HTTP proxy client. - /// - /// - /// An HTTP proxy client. - /// - public class HttpProxyClient : ProxyClient - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// -or- - /// The length of is greater than 255 characters. - /// - public HttpProxyClient (string host, int port) : base (host, port) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// The credentials to use to authenticate with the proxy server. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// -or- - /// The length of is greater than 255 characters. - /// - public HttpProxyClient (string host, int port, NetworkCredential credentials) : base (host, port, credentials) - { - } - - async Task ConnectAsync (string host, int port, bool doAsync, CancellationToken cancellationToken) - { - ValidateArguments (host, port); - - cancellationToken.ThrowIfCancellationRequested (); - - var socket = await SocketUtils.ConnectAsync (ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait (false); - var builder = new StringBuilder (); - - builder.AppendFormat (CultureInfo.InvariantCulture, "CONNECT {0}:{1} HTTP/1.1\r\n", host, port); - builder.AppendFormat (CultureInfo.InvariantCulture, "Host: {0}:{1}\r\n", host, port); - if (ProxyCredentials != null) { - var token = Encoding.UTF8.GetBytes (string.Format (CultureInfo.InvariantCulture, "{0}:{1}", ProxyCredentials.UserName, ProxyCredentials.Password)); - var base64 = Convert.ToBase64String (token); - builder.AppendFormat (CultureInfo.InvariantCulture, "Proxy-Authorization: Basic {0}\r\n", base64); - } - builder.Append ("\r\n"); - - var command = Encoding.UTF8.GetBytes (builder.ToString ()); - - try { - await SendAsync (socket, command, 0, command.Length, doAsync, cancellationToken).ConfigureAwait (false); - - var buffer = new byte[1024]; - var endOfHeaders = false; - var newline = false; - - builder.Clear (); - - // read until we consume the end of the headers (it's ok if we read some of the content) - do { - int nread = await ReceiveAsync (socket, buffer, 0, buffer.Length, doAsync, cancellationToken).ConfigureAwait (false); - - if (nread > 0) { - int n = nread; - - for (int i = 0; i < nread && !endOfHeaders; i++) { - switch ((char) buffer[i]) { - case '\r': - break; - case '\n': - endOfHeaders = newline; - newline = true; - - if (endOfHeaders) - n = i + 1; - break; - default: - newline = false; - break; - } - } - - builder.Append (Encoding.UTF8.GetString (buffer, 0, n)); - } - } while (!endOfHeaders); - - int index = 0; - - while (builder[index] != '\n') - index++; - - if (index > 0 && builder[index - 1] == '\r') - index--; - - // trim everything beyond the "HTTP/1.1 200 ..." part of the response - builder.Length = index; - - var response = builder.ToString (); - - if (response.Length >= 15 && response.StartsWith ("HTTP/1.", StringComparison.OrdinalIgnoreCase) && - (response[7] == '1' || response[7] == '0') && response[8] == ' ' && - response[9] == '2' && response[10] == '0' && response[11] == '0' && - response[12] == ' ') { - return socket; - } - - throw new ProxyProtocolException (string.Format (CultureInfo.InvariantCulture, "Failed to connect to {0}:{1}: {2}", host, port, response)); - } catch { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (socket.Connected) - socket.Disconnect (false); -#endif - socket.Dispose (); - throw; - } - } - - /// - /// Connect to the target host. - /// - /// - /// Connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public override Socket Connect (string host, int port, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously connect to the target host. - /// - /// - /// Asynchronously connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public override Task ConnectAsync (string host, int port, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Proxy/IProxyClient.cs b/src/MailKit/Net/Proxy/IProxyClient.cs deleted file mode 100644 index 78cbfcc..0000000 --- a/src/MailKit/Net/Proxy/IProxyClient.cs +++ /dev/null @@ -1,208 +0,0 @@ -// -// IProxyClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Threading; -using System.Net.Sockets; -using System.Threading.Tasks; - -namespace MailKit.Net.Proxy -{ - /// - /// An interface for connecting to services via a proxy. - /// - /// - /// Implemented by - /// and . - /// - public interface IProxyClient - { - /// - /// Gets the proxy credentials. - /// - /// - /// Gets the credentials to use when authenticating with the proxy server. - /// - /// The proxy credentials. - NetworkCredential ProxyCredentials { get; } - - /// - /// Get the proxy host. - /// - /// - /// Gets the host name of the proxy server. - /// - /// The host name of the proxy server. - string ProxyHost { get; } - - /// - /// Get the proxy port. - /// - /// - /// Gets the port to use when connecting to the proxy server. - /// - /// The proxy port. - int ProxyPort { get; } - - /// - /// Get or set the local IP end point to use when connecting to a remote host. - /// - /// - /// Gets or sets the local IP end point to use when connecting to a remote host. - /// - /// The local IP end point or null to use the default end point. - IPEndPoint LocalEndPoint { get; set; } - - /// - /// Connect to the target host. - /// - /// - /// Connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - Socket Connect (string host, int port, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously connect to the target host. - /// - /// - /// Asynchronously connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - Task ConnectAsync (string host, int port, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Connect to the target host. - /// - /// - /// Connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The timeout, in milliseconds. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The operation timed out. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - Socket Connect (string host, int port, int timeout, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously connect to the target host. - /// - /// - /// Asynchronously connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The timeout, in milliseconds. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The operation timed out. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - Task ConnectAsync (string host, int port, int timeout, CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MailKit/Net/Proxy/ProxyClient.cs b/src/MailKit/Net/Proxy/ProxyClient.cs deleted file mode 100644 index 86c4723..0000000 --- a/src/MailKit/Net/Proxy/ProxyClient.cs +++ /dev/null @@ -1,406 +0,0 @@ -// -// ProxyClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Threading; -using System.Net.Sockets; -using System.Threading.Tasks; - -namespace MailKit.Net.Proxy -{ - /// - /// An abstract proxy client base class. - /// - /// - /// A proxy client can be used to connect to a service through a firewall that - /// would otherwise be blocked. - /// - public abstract class ProxyClient : IProxyClient - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// -or- - /// The length of is greater than 255 characters. - /// - protected ProxyClient (string host, int port) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0 || host.Length > 255) - throw new ArgumentException ("The length of the host name must be between 0 and 256 characters.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - ProxyHost = host; - ProxyPort = port == 0 ? 1080 : port; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// The credentials to use to authenticate with the proxy server. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// -or- - /// The length of is greater than 255 characters. - /// - protected ProxyClient (string host, int port, NetworkCredential credentials) : this (host, port) - { - if (credentials == null) - throw new ArgumentNullException (nameof (credentials)); - - ProxyCredentials = credentials; - } - - /// - /// Gets the proxy credentials. - /// - /// - /// Gets the credentials to use when authenticating with the proxy server. - /// - /// The proxy credentials. - public NetworkCredential ProxyCredentials { - get; private set; - } - - /// - /// Get the proxy host. - /// - /// - /// Gets the host name of the proxy server. - /// - /// The host name of the proxy server. - public string ProxyHost { - get; private set; - } - - /// - /// Get the proxy port. - /// - /// - /// Gets the port to use when connecting to the proxy server. - /// - /// The proxy port. - public int ProxyPort { - get; private set; - } - - /// - /// Get or set the local IP end point to use when connecting to a remote host. - /// - /// - /// Gets or sets the local IP end point to use when connecting to a remote host. - /// - /// The local IP end point or null to use the default end point. - public IPEndPoint LocalEndPoint { - get; set; - } - - internal static void ValidateArguments (string host, int port) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0 || host.Length > 255) - throw new ArgumentException ("The length of the host name must be between 0 and 256 characters.", nameof (host)); - - if (port <= 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - } - - static void ValidateArguments (string host, int port, int timeout) - { - ValidateArguments (host, port); - - if (timeout < -1) - throw new ArgumentOutOfRangeException (nameof (timeout)); - } - - static void AsyncOperationCompleted (object sender, SocketAsyncEventArgs args) - { - var tcs = (TaskCompletionSource) args.UserToken; - - if (args.SocketError == SocketError.Success) { - tcs.TrySetResult (true); - return; - } - - tcs.TrySetException (new SocketException ((int) args.SocketError)); - } - - internal static async Task SendAsync (Socket socket, byte[] buffer, int offset, int length, bool doAsync, CancellationToken cancellationToken) - { - if (doAsync || cancellationToken.CanBeCanceled) { - var tcs = new TaskCompletionSource (); - - using (var registration = cancellationToken.Register (() => tcs.TrySetCanceled (), false)) { - using (var args = new SocketAsyncEventArgs ()) { - args.Completed += AsyncOperationCompleted; - args.SetBuffer (buffer, offset, length); - args.AcceptSocket = socket; - args.UserToken = tcs; - - if (!socket.SendAsync (args)) - AsyncOperationCompleted (null, args); - - if (doAsync) - await tcs.Task.ConfigureAwait (false); - else - tcs.Task.GetAwaiter ().GetResult (); - - return; - } - } - } - - SocketUtils.Poll (socket, SelectMode.SelectWrite, cancellationToken); - - socket.Send (buffer, offset, length, SocketFlags.None); - } - - internal static async Task ReceiveAsync (Socket socket, byte[] buffer, int offset, int length, bool doAsync, CancellationToken cancellationToken) - { - if (doAsync || cancellationToken.CanBeCanceled) { - var tcs = new TaskCompletionSource (); - - using (var registration = cancellationToken.Register (() => tcs.TrySetCanceled (), false)) { - using (var args = new SocketAsyncEventArgs ()) { - args.Completed += AsyncOperationCompleted; - args.SetBuffer (buffer, offset, length); - args.AcceptSocket = socket; - args.UserToken = tcs; - - if (!socket.ReceiveAsync (args)) - AsyncOperationCompleted (null, args); - - if (doAsync) - await tcs.Task.ConfigureAwait (false); - else - tcs.Task.GetAwaiter ().GetResult (); - - return args.BytesTransferred; - } - } - } - - SocketUtils.Poll (socket, SelectMode.SelectRead, cancellationToken); - - return socket.Receive (buffer, offset, length, SocketFlags.None); - } - - /// - /// Connect to the target host. - /// - /// - /// Connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public abstract Socket Connect (string host, int port, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously connect to the target host. - /// - /// - /// Asynchronously connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public abstract Task ConnectAsync (string host, int port, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Connect to the target host. - /// - /// - /// Connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The timeout, in milliseconds. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// -or- - /// is less than -1. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The operation timed out. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public virtual Socket Connect (string host, int port, int timeout, CancellationToken cancellationToken = default (CancellationToken)) - { - ValidateArguments (host, port, timeout); - - using (var ts = new CancellationTokenSource (timeout)) { - using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, ts.Token)) { - try { - return Connect (host, port, linked.Token); - } catch (OperationCanceledException) { - if (!cancellationToken.IsCancellationRequested) - throw new TimeoutException (); - throw; - } - } - } - } - - /// - /// Asynchronously connect to the target host. - /// - /// - /// Asynchronously connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The timeout, in milliseconds. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// -or- - /// is less than -1. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// The operation timed out. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public async virtual Task ConnectAsync (string host, int port, int timeout, CancellationToken cancellationToken = default (CancellationToken)) - { - ValidateArguments (host, port, timeout); - - using (var ts = new CancellationTokenSource (timeout)) { - using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, ts.Token)) { - try { - return await ConnectAsync (host, port, linked.Token).ConfigureAwait (false); - } catch (OperationCanceledException) { - if (!cancellationToken.IsCancellationRequested) - throw new TimeoutException (); - throw; - } - } - } - } - } -} diff --git a/src/MailKit/Net/Proxy/ProxyProtocolException.cs b/src/MailKit/Net/Proxy/ProxyProtocolException.cs deleted file mode 100644 index 0e1ee47..0000000 --- a/src/MailKit/Net/Proxy/ProxyProtocolException.cs +++ /dev/null @@ -1,97 +0,0 @@ -// -// ProxyProtocolException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit.Net.Proxy -{ - /// - /// A proxy protocol exception. - /// - /// - /// The exception that is thrown when there is an error communicating with a proxy server. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class ProxyProtocolException : ProtocolException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the serialized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected ProxyProtocolException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// An inner exception. - public ProxyProtocolException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public ProxyProtocolException (string message) : base (message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public ProxyProtocolException () - { - } - } -} diff --git a/src/MailKit/Net/Proxy/Socks4Client.cs b/src/MailKit/Net/Proxy/Socks4Client.cs deleted file mode 100644 index bcd3969..0000000 --- a/src/MailKit/Net/Proxy/Socks4Client.cs +++ /dev/null @@ -1,299 +0,0 @@ -// -// Socks4Client.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Globalization; -using System.Threading.Tasks; - -namespace MailKit.Net.Proxy -{ - /// - /// A SOCKS4 proxy client. - /// - /// - /// A SOCKS4 proxy client. - /// - public class Socks4Client : SocksClient - { - static readonly byte[] InvalidIPAddress = { 0, 0, 0, 1 }; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// -or- - /// The length of is greater than 255 characters. - /// - public Socks4Client (string host, int port) : base (4, host, port) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// The credentials to use to authenticate with the proxy server. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// - public Socks4Client (string host, int port, NetworkCredential credentials) : base (4, host, port, credentials) - { - } - - /// - /// Get or set whether this is a Socks4a client. - /// - /// - /// Gets or sets whether this is a Socks4a client. - /// - /// true if is is a Socks4a client; otherwise, false. - protected bool IsSocks4a { - get; set; - } - - enum Socks4Command : byte - { - Connect = 0x01, - Bind = 0x02, - } - - enum Socks4Reply : byte - { - RequestGranted = 0x5a, - RequestRejected = 0x5b, - RequestFailedNoIdentd = 0x5c, - RequestFailedWrongId = 0x5d - } - - static string GetFailureReason (byte reply) - { - switch ((Socks4Reply) reply) { - case Socks4Reply.RequestRejected: return "Request rejected or failed."; - case Socks4Reply.RequestFailedNoIdentd: return "Request failed; unable to contact client machine's identd service."; - case Socks4Reply.RequestFailedWrongId: return "Request failed; client ID does not match specified username."; - default: return "Unknown error."; - } - } - - async Task ResolveAsync (string host, bool doAsync, CancellationToken cancellationToken) - { - IPAddress[] ipAddresses; - - if (doAsync) { - ipAddresses = await Dns.GetHostAddressesAsync (host).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - ipAddresses = Dns.GetHostAddressesAsync (host).GetAwaiter ().GetResult (); -#else - ipAddresses = Dns.GetHostAddresses (host); -#endif - } - - for (int i = 0; i < ipAddresses.Length; i++) { - if (ipAddresses[i].AddressFamily == AddressFamily.InterNetwork) - return ipAddresses[i]; - } - - throw new ArgumentException ($"Could not resolve a suitable IPv4 address for '{host}'.", nameof (host)); - } - - async Task ConnectAsync (string host, int port, bool doAsync, CancellationToken cancellationToken) - { - byte[] addr, domain = null; - IPAddress ip; - - ValidateArguments (host, port); - - if (!IPAddress.TryParse (host, out ip)) { - if (IsSocks4a) { - domain = Encoding.UTF8.GetBytes (host); - addr = InvalidIPAddress; - } else { - ip = await ResolveAsync (host, doAsync, cancellationToken).ConfigureAwait (false); - addr = ip.GetAddressBytes (); - } - } else { - if (ip.AddressFamily != AddressFamily.InterNetwork) - throw new ArgumentException ("The specified host address must be IPv4.", nameof (host)); - - addr = ip.GetAddressBytes (); - } - - cancellationToken.ThrowIfCancellationRequested (); - - var socket = await SocketUtils.ConnectAsync (ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait (false); - var user = ProxyCredentials != null ? Encoding.UTF8.GetBytes (ProxyCredentials.UserName) : new byte[0]; - - try { - // +----+-----+----------+----------+----------+-------+--------------+-------+ - // |VER | CMD | DST.PORT | DST.ADDR | USERID | NULL | DST.DOMAIN | NULL | - // +----+-----+----------+----------+----------+-------+--------------+-------+ - // | 1 | 1 | 2 | 4 | VARIABLE | X'00' | VARIABLE | X'00' | - // +----+-----+----------+----------+----------+-------+--------------+-------+ - int bufferSize = 9 + user.Length + (domain != null ? domain.Length + 1 : 0); - var buffer = new byte[bufferSize]; - int nread, n = 0; - - buffer[n++] = (byte) SocksVersion; - buffer[n++] = (byte) Socks4Command.Connect; - buffer[n++] = (byte)(port >> 8); - buffer[n++] = (byte) port; - Buffer.BlockCopy (addr, 0, buffer, n, 4); - n += 4; - Buffer.BlockCopy (user, 0, buffer, n, user.Length); - n += user.Length; - buffer[n++] = 0x00; - if (domain != null) { - Buffer.BlockCopy (domain, 0, buffer, n, domain.Length); - n += domain.Length; - buffer[n++] = 0x00; - } - - await SendAsync (socket, buffer, 0, n, doAsync, cancellationToken).ConfigureAwait (false); - - // +-----+-----+----------+----------+ - // | VER | REP | BND.PORT | BND.ADDR | - // +-----+-----+----------+----------+ - // | 1 | 1 | 2 | 4 | - // +-----+-----+----------+----------+ - n = 0; - - do { - if ((nread = await ReceiveAsync (socket, buffer, 0 + n, 8 - n, doAsync, cancellationToken).ConfigureAwait (false)) > 0) - n += nread; - } while (n < 8); - - if (buffer[1] != (byte) Socks4Reply.RequestGranted) - throw new ProxyProtocolException (string.Format (CultureInfo.InvariantCulture, "Failed to connect to {0}:{1}: {2}", host, port, GetFailureReason (buffer[1]))); - - // TODO: do we care about BND.ADDR and BND.PORT? - - return socket; - } catch { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (socket.Connected) - socket.Disconnect (false); -#endif - socket.Dispose (); - throw; - } - } - - /// - /// Connect to the target host. - /// - /// - /// Connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public override Socket Connect (string host, int port, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously connect to the target host. - /// - /// - /// Asynchronously connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public override Task ConnectAsync (string host, int port, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Proxy/Socks4aClient.cs b/src/MailKit/Net/Proxy/Socks4aClient.cs deleted file mode 100644 index 463db6e..0000000 --- a/src/MailKit/Net/Proxy/Socks4aClient.cs +++ /dev/null @@ -1,88 +0,0 @@ -// -// Socks4aClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Net; - -namespace MailKit.Net.Proxy -{ - /// - /// A SOCKS4a proxy client. - /// - /// - /// A SOCKS4a proxy client. - /// - public class Socks4aClient : Socks4Client - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// -or- - /// The length of is greater than 255 characters. - /// - public Socks4aClient (string host, int port) : base (host, port) - { - IsSocks4a = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// The credentials to use to authenticate with the proxy server. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// - public Socks4aClient (string host, int port, NetworkCredential credentials) : base (host, port, credentials) - { - IsSocks4a = true; - } - } -} diff --git a/src/MailKit/Net/Proxy/Socks5Client.cs b/src/MailKit/Net/Proxy/Socks5Client.cs deleted file mode 100644 index 54a2fb6..0000000 --- a/src/MailKit/Net/Proxy/Socks5Client.cs +++ /dev/null @@ -1,421 +0,0 @@ -// -// Socks5Client.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Globalization; -using System.Threading.Tasks; - -using MailKit.Security; - -namespace MailKit.Net.Proxy -{ - /// - /// A SOCKS5 proxy client. - /// - /// - /// A SOCKS5 proxy client. - /// - public class Socks5Client : SocksClient - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// -or- - /// The length of is greater than 255 characters. - /// - public Socks5Client (string host, int port) : base (5, host, port) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The host name of the proxy server. - /// The proxy server port. - /// The credentials to use to authenticate with the proxy server. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// -or- - /// The length of is greater than 255 characters. - /// - public Socks5Client (string host, int port, NetworkCredential credentials) : base (5, host, port, credentials) - { - } - - internal enum Socks5AddressType : byte - { - None = 0x00, - IPv4 = 0x01, - Domain = 0x03, - IPv6 = 0x04 - } - - enum Socks5AuthMethod : byte - { - Anonymous = 0x00, - GSSAPI = 0x01, - UserPassword = 0x02, - NotSupported = 0xff - } - - enum Socks5Command : byte - { - Connect = 0x01, - Bind = 0x02, - UdpAssociate = 0x03, - } - - internal enum Socks5Reply : byte - { - Success = 0x00, - GeneralServerFailure = 0x01, - ConnectionNotAllowed = 0x02, - NetworkUnreachable = 0x03, - HostUnreachable = 0x04, - ConnectionRefused = 0x05, - TTLExpired = 0x06, - CommandNotSupported = 0x07, - AddressTypeNotSupported = 0x08 - } - - internal static string GetFailureReason (byte reply) - { - switch ((Socks5Reply) reply) { - case Socks5Reply.GeneralServerFailure: return "General server failure."; - case Socks5Reply.ConnectionNotAllowed: return "Connection not allowed."; - case Socks5Reply.NetworkUnreachable: return "Network unreachable."; - case Socks5Reply.HostUnreachable: return "Host unreachable."; - case Socks5Reply.ConnectionRefused: return "Connection refused."; - case Socks5Reply.TTLExpired: return "TTL expired."; - case Socks5Reply.CommandNotSupported: return "Command not supported."; - case Socks5Reply.AddressTypeNotSupported: return "Address type not supported."; - default: return string.Format (CultureInfo.InvariantCulture, "Unknown error ({0}).", (int) reply); - } - } - - internal static Socks5AddressType GetAddressType (string host, out IPAddress ip) - { - if (!IPAddress.TryParse (host, out ip)) - return Socks5AddressType.Domain; - - switch (ip.AddressFamily) { - case AddressFamily.InterNetworkV6: return Socks5AddressType.IPv6; - case AddressFamily.InterNetwork: return Socks5AddressType.IPv4; - default: throw new ArgumentException ("The host address must be an IPv4 or IPv6 address.", nameof (host)); - } - } - - void VerifySocksVersion (byte version) - { - if (version != (byte) SocksVersion) - throw new ProxyProtocolException (string.Format (CultureInfo.InvariantCulture, "Proxy server responded with unknown SOCKS version: {0}", (int) version)); - } - - async Task NegotiateAuthMethodAsync (Socket socket, bool doAsync, CancellationToken cancellationToken, params Socks5AuthMethod[] methods) - { - // +-----+----------+----------+ - // | VER | NMETHODS | METHODS | - // +-----+----------+----------+ - // | 1 | 1 | 1 to 255 | - // +-----+----------+----------+ - var buffer = new byte[2 + methods.Length]; - int nread, n = 0; - - buffer[n++] = (byte) SocksVersion; - buffer[n++] = (byte) methods.Length; - for (int i = 0; i < methods.Length; i++) - buffer[n++] = (byte) methods[i]; - - await SendAsync (socket, buffer, 0, n, doAsync, cancellationToken).ConfigureAwait (false); - - // +-----+--------+ - // | VER | METHOD | - // +-----+--------+ - // | 1 | 1 | - // +-----+--------+ - n = 0; - do { - if ((nread = await ReceiveAsync (socket, buffer, 0 + n, 2 - n, doAsync, cancellationToken).ConfigureAwait (false)) > 0) - n += nread; - } while (n < 2); - - VerifySocksVersion (buffer[0]); - - return (Socks5AuthMethod) buffer[1]; - } - - async Task AuthenticateAsync (Socket socket, bool doAsync, CancellationToken cancellationToken) - { - var user = Encoding.UTF8.GetBytes (ProxyCredentials.UserName); - - if (user.Length > 255) - throw new AuthenticationException ("User name too long."); - - var passwd = Encoding.UTF8.GetBytes (ProxyCredentials.Password); - - if (passwd.Length > 255) { - Array.Clear (passwd, 0, passwd.Length); - throw new AuthenticationException ("Password too long."); - } - - var buffer = new byte[user.Length + passwd.Length + 3]; - int nread, n = 0; - - buffer[n++] = 1; - buffer[n++] = (byte) user.Length; - Buffer.BlockCopy (user, 0, buffer, n, user.Length); - n += user.Length; - buffer[n++] = (byte) passwd.Length; - Buffer.BlockCopy (passwd, 0, buffer, n, passwd.Length); - n += passwd.Length; - - Array.Clear (passwd, 0, passwd.Length); - - await SendAsync (socket, buffer, 0, n, doAsync, cancellationToken).ConfigureAwait (false); - - n = 0; - do { - if ((nread = await ReceiveAsync (socket, buffer, 0 + n, 2 - n, doAsync, cancellationToken).ConfigureAwait (false)) > 0) - n += nread; - } while (n < 2); - - if (buffer[1] != (byte) Socks5Reply.Success) - throw new AuthenticationException ("Failed to authenticate with SOCKS5 proxy server."); - } - - async Task ConnectAsync (string host, int port, bool doAsync, CancellationToken cancellationToken) - { - Socks5AddressType addrType; - IPAddress ip; - - ValidateArguments (host, port); - - addrType = GetAddressType (host, out ip); - - cancellationToken.ThrowIfCancellationRequested (); - - var socket = await SocketUtils.ConnectAsync (ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait (false); - byte[] domain = null, addr = null; - const int bufferSize = 6 + 257; - - if (addrType == Socks5AddressType.Domain) - domain = Encoding.UTF8.GetBytes (host); - - try { - Socks5AuthMethod method; - - if (ProxyCredentials != null) - method = await NegotiateAuthMethodAsync (socket, doAsync, cancellationToken, Socks5AuthMethod.UserPassword, Socks5AuthMethod.Anonymous).ConfigureAwait (false); - else - method = await NegotiateAuthMethodAsync (socket, doAsync, cancellationToken, Socks5AuthMethod.Anonymous).ConfigureAwait (false); - - switch (method) { - case Socks5AuthMethod.UserPassword: - await AuthenticateAsync (socket, doAsync, cancellationToken).ConfigureAwait (false); - break; - case Socks5AuthMethod.Anonymous: - break; - default: - throw new ProxyProtocolException ("Failed to negotiate authentication method with the proxy server."); - } - - // +----+-----+-------+------+----------+----------+ - // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ - var buffer = new byte[bufferSize]; - int nread, n = 0; - - buffer[n++] = (byte) SocksVersion; - buffer[n++] = (byte) Socks5Command.Connect; - buffer[n++] = 0x00; - buffer[n++] = (byte) addrType; - switch (addrType) { - case Socks5AddressType.Domain: - buffer[n++] = (byte) domain.Length; - Buffer.BlockCopy (domain, 0, buffer, n, domain.Length); - n += domain.Length; - break; - case Socks5AddressType.IPv6: - addr = ip.GetAddressBytes (); - Buffer.BlockCopy (addr, 0, buffer, n, addr.Length); - n += 16; - break; - case Socks5AddressType.IPv4: - addr = ip.GetAddressBytes (); - Buffer.BlockCopy (addr, 0, buffer, n, addr.Length); - n += 4; - break; - } - buffer[n++] = (byte)(port >> 8); - buffer[n++] = (byte) port; - - await SendAsync (socket, buffer, 0, n, doAsync, cancellationToken).ConfigureAwait (false); - - // +-----+-----+-------+------+----------+----------+ - // | VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - // +-----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +-----+-----+-------+------+----------+----------+ - - // Note: We know we'll need at least 4 bytes of header + a minimum of 1 byte - // to determine the length of the BND.ADDR field if ATYP is a domain. - int need = 5; - n = 0; - - do { - if ((nread = await ReceiveAsync (socket, buffer, 0 + n, need - n, doAsync, cancellationToken).ConfigureAwait (false)) > 0) - n += nread; - } while (n < need); - - VerifySocksVersion (buffer[0]); - - if (buffer[1] != (byte) Socks5Reply.Success) - throw new ProxyProtocolException (string.Format (CultureInfo.InvariantCulture, "Failed to connect to {0}:{1}: {2}", host, port, GetFailureReason (buffer[1]))); - - addrType = (Socks5AddressType) buffer[3]; - - switch (addrType) { - case Socks5AddressType.Domain: need += buffer[4] + 2; break; - case Socks5AddressType.IPv6: need += (16 - 1) + 2; break; - case Socks5AddressType.IPv4: need += (4 - 1) + 2; break; - default: throw new ProxyProtocolException ("Proxy server returned unknown address type."); - } - - do { - if ((nread = await ReceiveAsync (socket, buffer, 0 + n, need - n, doAsync, cancellationToken).ConfigureAwait (false)) > 0) - n += nread; - } while (n < need); - - // TODO: do we care about BND.ADDR and BND.PORT? - - return socket; - } catch { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (socket.Connected) - socket.Disconnect (false); -#endif - socket.Dispose (); - throw; - } - } - - /// - /// Connect to the target host. - /// - /// - /// Connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public override Socket Connect (string host, int port, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously connect to the target host. - /// - /// - /// Asynchronously connects to the target host and port through the proxy server. - /// - /// The connected socket. - /// The host name of the proxy server. - /// The proxy server port. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An I/O error occurred. - /// - public override Task ConnectAsync (string host, int port, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Proxy/SocksClient.cs b/src/MailKit/Net/Proxy/SocksClient.cs deleted file mode 100644 index 4c91345..0000000 --- a/src/MailKit/Net/Proxy/SocksClient.cs +++ /dev/null @@ -1,100 +0,0 @@ -// -// SocksClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; - -namespace MailKit.Net.Proxy -{ - /// - /// An abstract SOCKS proxy client. - /// - /// - /// An abstract SOCKS proxy client. - /// - public abstract class SocksClient : ProxyClient - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The SOCKS protocol version. - /// The host name of the proxy server. - /// The proxy server port. - /// - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// - protected SocksClient (int version, string host, int port) : base (host, port) - { - SocksVersion = version; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a new instance of the class. - /// - /// The SOCKS protocol version. - /// The host name of the proxy server. - /// The proxy server port. - /// The credentials to use to authenticate with the proxy server. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 1 and 65535. - /// - /// - /// The is a zero-length string. - /// - protected SocksClient (int version, string host, int port, NetworkCredential credentials) : base (host, port, credentials) - { - SocksVersion = version; - } - - /// - /// Get the SOCKS protocol version. - /// - /// - /// Gets the SOCKS protocol version. - /// - /// The SOCKS protocol version. - public int SocksVersion { - get; private set; - } - } -} diff --git a/src/MailKit/Net/SelectMode.cs b/src/MailKit/Net/SelectMode.cs deleted file mode 100644 index 893ea9f..0000000 --- a/src/MailKit/Net/SelectMode.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -// SelectMode.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2019 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Net -{ - enum SelectMode { - SelectRead, - SelectWrite, - SelectError - } -} diff --git a/src/MailKit/Net/Smtp/AsyncSmtpClient.cs b/src/MailKit/Net/Smtp/AsyncSmtpClient.cs deleted file mode 100644 index 603bcbc..0000000 --- a/src/MailKit/Net/Smtp/AsyncSmtpClient.cs +++ /dev/null @@ -1,689 +0,0 @@ -// -// AsyncSmtpClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit; - -using MailKit.Security; - -namespace MailKit.Net.Smtp -{ - public partial class SmtpClient - { - /// - /// Asynchronously send a custom command to the SMTP server. - /// - /// - /// Asynchronously sends a custom command to the SMTP server. - /// The command string should not include the terminating \r\n sequence. - /// - /// The command response. - /// The command. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - protected Task SendCommandAsync (string command, CancellationToken cancellationToken = default (CancellationToken)) - { - if (command == null) - throw new ArgumentNullException (nameof (command)); - - CheckDisposed (); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The SmtpClient must be connected before you can send commands."); - - return SendCommandAsync (command, true, cancellationToken); - } - - /// - /// Asynchronously authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// An asynchronous task context. - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The SMTP server does not support authentication. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override Task AuthenticateAsync (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)) - { - return AuthenticateAsync (mechanism, true, cancellationToken); - } - - /// - /// Asynchronously authenticate using the supplied credentials. - /// - /// - /// If the SMTP server supports authentication, then the SASL mechanisms - /// that both the client and server support are tried in order of greatest - /// security to weakest security. Once a SASL authentication mechanism is - /// found that both client and server support, the credentials are used to - /// authenticate. - /// If, on the other hand, authentication is not supported by the SMTP - /// server, then this method will throw . - /// The property can be checked for the - /// flag to make sure the - /// SMTP server supports authentication before calling this method. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// An asynchronous task context. - /// The text encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The SMTP server does not support authentication. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override Task AuthenticateAsync (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)) - { - return AuthenticateAsync (encoding, credentials, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified SMTP or SMTP/S server. - /// - /// - /// Establishes a connection to the specified SMTP or SMTP/S server. - /// If the has a value of 0, then the - /// parameter is used to determine the default port to - /// connect to. The default port used with - /// is 465. All other values will use a default port of 25. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 465, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// The connection established by any of the - /// Connect - /// methods may be re-used if an application wishes to send multiple messages - /// to the same SMTP server. Since connecting and authenticating can be expensive - /// operations, re-using a connection can significantly improve performance when - /// sending a large number of messages to the same SMTP server over a short - /// period of time. - /// - /// - /// - /// - /// An asynchronous task context. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the SMTP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override Task ConnectAsync (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified SMTP or SMTP/S server using the provided socket. - /// - /// - /// Establishes a connection to the specified SMTP or SMTP/S server using the provided socket. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 465, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// An asynchronous task context. - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the SMTP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override Task ConnectAsync (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (socket, host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously establish a connection to the specified SMTP or SMTP/S server using the provided socket. - /// - /// - /// Establishes a connection to the specified SMTP or SMTP/S server using the provided socket. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 465, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// An asynchronous task context. - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the SMTP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override Task ConnectAsync (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - return ConnectAsync (stream, host, port, options, true, cancellationToken); - } - - /// - /// Asynchronously disconnect the service. - /// - /// - /// If is true, a QUIT command will be issued in order to disconnect cleanly. - /// - /// - /// - /// - /// An asynchronous task context. - /// If set to true, a QUIT command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - public override Task DisconnectAsync (bool quit, CancellationToken cancellationToken = default (CancellationToken)) - { - return DisconnectAsync (quit, true, cancellationToken); - } - - /// - /// Asynchronously ping the SMTP server to keep the connection alive. - /// - /// Mail servers, if left idle for too long, will automatically drop the connection. - /// An asynchronous task context. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override Task NoOpAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - return NoOpAsync (true, cancellationToken); - } - - /// - /// Asynchronously send the specified message. - /// - /// - /// Sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// - /// - /// - /// An asynchronous task context. - /// The formatting options. - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - public override Task SendAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - var recipients = GetMessageRecipients (message); - var sender = GetMessageSender (message); - - if (sender == null) - throw new InvalidOperationException ("No sender has been specified."); - - if (recipients.Count == 0) - throw new InvalidOperationException ("No recipients have been specified."); - - return SendAsync (options, message, sender, recipients, true, cancellationToken, progress); - } - - /// - /// Asynchronously send the specified message using the supplied sender and recipients. - /// - /// - /// Sends the message by uploading it to an SMTP server using the supplied sender and recipients. - /// - /// An asynchronous task context. - /// The formatting options. - /// The message. - /// The mailbox address to use for sending the message. - /// The mailbox addresses that should receive the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - public override Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (sender == null) - throw new ArgumentNullException (nameof (sender)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - var unique = new HashSet (StringComparer.OrdinalIgnoreCase); - var rcpts = new List (); - - AddUnique (rcpts, unique, recipients); - - if (rcpts.Count == 0) - throw new InvalidOperationException ("No recipients have been specified."); - - return SendAsync (options, message, sender, rcpts, true, cancellationToken, progress); - } - - /// - /// Asynchronously expand a mailing address alias. - /// - /// - /// Expands a mailing address alias. - /// - /// The expanded list of mailbox addresses. - /// The mailing address alias. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before verifying the existence of an address. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - public Task ExpandAsync (string alias, CancellationToken cancellationToken = default (CancellationToken)) - { - return ExpandAsync (alias, true, cancellationToken); - } - - /// - /// Asynchronously verify the existence of a mailbox address. - /// - /// - /// Verifies the existence a mailbox address with the SMTP server, returning the expanded - /// mailbox address if it exists. - /// - /// The expanded mailbox address. - /// The mailbox address. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before verifying the existence of an address. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - public Task VerifyAsync (string address, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (address, true, cancellationToken); - } - } -} diff --git a/src/MailKit/Net/Smtp/ISmtpClient.cs b/src/MailKit/Net/Smtp/ISmtpClient.cs deleted file mode 100644 index 33eb72f..0000000 --- a/src/MailKit/Net/Smtp/ISmtpClient.cs +++ /dev/null @@ -1,246 +0,0 @@ -// -// ISmtpClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Threading; -using System.Threading.Tasks; - -using MimeKit; - -namespace MailKit.Net.Smtp { - /// - /// An interface for an SMTP client. - /// - /// - /// Implemented by . - /// - public interface ISmtpClient : IMailTransport - { - /// - /// Get the capabilities supported by the SMTP server. - /// - /// - /// The capabilities will not be known until a successful connection has been made - /// and may change once the client is authenticated. - /// - /// - /// - /// - /// The capabilities. - /// - /// Capabilities cannot be enabled, they may only be disabled. - /// - SmtpCapabilities Capabilities { get; } - - /// - /// Get or set the local domain. - /// - /// - /// The local domain is used in the HELO or EHLO commands sent to - /// the SMTP server. If left unset, the local IP address will be - /// used instead. - /// - /// The local domain. - string LocalDomain { get; set; } - - /// - /// Get the maximum message size supported by the server. - /// - /// - /// The maximum message size will not be known until a successful connection has - /// been made and may change once the client is authenticated. - /// This value is only relevant if the includes - /// the flag. - /// - /// - /// - /// - /// The maximum message size supported by the server. - uint MaxSize { get; } - - /// - /// Get or set how much of the message to include in any failed delivery status notifications. - /// - /// - /// Gets or sets how much of the message to include in any failed delivery status notifications. - /// - /// A value indicating how much of the message to include in a failure delivery status notification. - DeliveryStatusNotificationType DeliveryStatusNotificationType { get; set; } - - /// - /// Expand a mailing address alias. - /// - /// - /// Expands a mailing address alias. - /// - /// The expanded list of mailbox addresses. - /// The mailing address alias. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before verifying the existence of an address. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - InternetAddressList Expand (string alias, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously expand a mailing address alias. - /// - /// - /// Expands a mailing address alias. - /// - /// The expanded list of mailbox addresses. - /// The mailing address alias. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before verifying the existence of an address. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - Task ExpandAsync (string alias, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Verify the existence of a mailbox address. - /// - /// - /// Verifies the existence a mailbox address with the SMTP server, returning the expanded - /// mailbox address if it exists. - /// - /// The expanded mailbox address. - /// The mailbox address. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before verifying the existence of an address. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - MailboxAddress Verify (string address, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously verify the existence of a mailbox address. - /// - /// - /// Verifies the existence a mailbox address with the SMTP server, returning the expanded - /// mailbox address if it exists. - /// - /// The expanded mailbox address. - /// The mailbox address. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before verifying the existence of an address. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - Task VerifyAsync (string address, CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MailKit/Net/Smtp/SmtpCapabilities.cs b/src/MailKit/Net/Smtp/SmtpCapabilities.cs deleted file mode 100644 index d0b664d..0000000 --- a/src/MailKit/Net/Smtp/SmtpCapabilities.cs +++ /dev/null @@ -1,106 +0,0 @@ -// -// SmtpCapabilities.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitations the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Net.Smtp { - /// - /// Capabilities supported by an SMTP server. - /// - /// - /// Capabilities are read as part of the response to the EHLO command that - /// is issued during the connection phase of the . - /// - /// - /// - /// - [Flags] - public enum SmtpCapabilities : uint { - /// - /// The server does not support any additional extensions. - /// - None = 0, - - /// - /// The server supports the SIZE extension - /// and may have a maximum message size limitation (see ). - /// - Size = 1 << 0, - - /// - /// The server supports the DSN extension, - /// allowing clients to specify which (if any) recipients they would like to receive delivery - /// notifications for. - /// - Dsn = 1 << 1, - - /// - /// The server supports the ENHANCEDSTATUSCODES - /// extension. - /// - EnhancedStatusCodes = 1 << 2, - - /// - /// The server supports the AUTH extension, - /// allowing clients to authenticate via supported SASL mechanisms. - /// - Authentication = 1 << 3, - - /// - /// The server supports the 8BITMIME extension, - /// allowing clients to send messages using the "8bit" Content-Transfer-Encoding. - /// - EightBitMime = 1 << 4, - - /// - /// The server supports the PIPELINING extension, - /// allowing clients to send multiple commands at once in order to reduce round-trip latency. - /// - Pipelining = 1 << 5, - - /// - /// The server supports the BINARYMIME extension. - /// - BinaryMime = 1 << 6, - - /// - /// The server supports the CHUNKING extension, - /// allowing clients to upload messages in chunks. - /// - Chunking = 1 << 7, - - /// - /// The server supports the STARTTLS extension, - /// allowing clients to switch to an encrypted SSL/TLS connection after connecting. - /// - StartTLS = 1 << 8, - - /// - /// The server supports the SMTPUTF8 extension. - /// - UTF8 = 1 << 9, - } -} diff --git a/src/MailKit/Net/Smtp/SmtpClient.cs b/src/MailKit/Net/Smtp/SmtpClient.cs deleted file mode 100644 index c611f27..0000000 --- a/src/MailKit/Net/Smtp/SmtpClient.cs +++ /dev/null @@ -1,2465 +0,0 @@ -// -// SmtpClient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Linq; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Net.Security; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; - -using MimeKit; -using MimeKit.IO; -using MimeKit.Cryptography; - -using MailKit.Security; - -using SslStream = MailKit.Net.SslStream; -using NetworkStream = MailKit.Net.NetworkStream; - -namespace MailKit.Net.Smtp { - /// - /// An SMTP client that can be used to send email messages. - /// - /// - /// The class supports both the "smtp" and "smtps" protocols. The "smtp" - /// protocol makes a clear-text connection to the SMTP server and does not use SSL or TLS unless the SMTP - /// server supports the STARTTLS extension. The "smtps" - /// protocol, however, connects to the SMTP server using an SSL-wrapped connection. - /// The connection established by any of the - /// Connect methods may be re-used if an - /// application wishes to send multiple messages to the same SMTP server. Since connecting and authenticating - /// can be expensive operations, re-using a connection can significantly improve performance when sending a - /// large number of messages to the same SMTP server over a short period of time. - /// - /// - /// - /// - public partial class SmtpClient : MailTransport, ISmtpClient - { - static readonly byte[] EndData = Encoding.ASCII.GetBytes (".\r\n"); - const int MaxLineLength = 998; - - enum SmtpCommand { - MailFrom, - RcptTo - } - - readonly HashSet authenticationMechanisms = new HashSet (StringComparer.Ordinal); - readonly List queued = new List (); - SmtpCapabilities capabilities; - int timeout = 2 * 60 * 1000; - bool authenticated; - bool connected; - bool disposed; - bool secure; - Uri uri; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Before you can send messages with the , you must first call one of - /// the Connect methods. - /// Depending on whether the SMTP server requires authenticating or not, you may also need to - /// authenticate using one of the - /// Authenticate methods. - /// - /// - /// - /// - public SmtpClient () : this (new NullProtocolLogger ()) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Before you can send messages with the , you must first call one of - /// the Connect methods. - /// Depending on whether the SMTP server requires authenticating or not, you may also need to - /// authenticate using one of the - /// Authenticate methods. - /// - /// The protocol logger. - /// - /// is null. - /// - /// - /// - /// - public SmtpClient (IProtocolLogger protocolLogger) : base (protocolLogger) - { - } - - /// - /// Get the underlying SMTP stream. - /// - /// - /// Gets the underlying SMTP stream. - /// - /// The SMTP stream. - SmtpStream Stream { - get; set; - } - - /// - /// Gets an object that can be used to synchronize access to the SMTP server. - /// - /// - /// Gets an object that can be used to synchronize access to the SMTP server between multiple threads. - /// When using methods from multiple threads, it is important to lock the - /// object for thread safety. - /// - /// The lock object. - public override object SyncRoot { - get { return this; } - } - - /// - /// Get the protocol supported by the message service. - /// - /// - /// Gets the protocol supported by the message service. - /// - /// The protocol. - protected override string Protocol { - get { return "smtp"; } - } - - /// - /// Get the capabilities supported by the SMTP server. - /// - /// - /// The capabilities will not be known until a successful connection has been made - /// and may change once the client is authenticated. - /// - /// - /// - /// - /// The capabilities. - /// - /// Capabilities cannot be enabled, they may only be disabled. - /// - public SmtpCapabilities Capabilities { - get { return capabilities; } - set { - if ((capabilities | value) > capabilities) - throw new ArgumentException ("Capabilities cannot be enabled, they may only be disabled.", nameof (value)); - - capabilities = value; - } - } - - /// - /// Get or set the local domain. - /// - /// - /// The local domain is used in the HELO or EHLO commands sent to - /// the SMTP server. If left unset, the local IP address will be - /// used instead. - /// - /// The local domain. - public string LocalDomain { - get; set; - } - - /// - /// Get whether or not the BDAT command is preferred over the DATA command. - /// - /// - /// Gets whether or not the BDAT command is preferred over the standard DATA - /// command. - /// The BDAT command is normally only used when the message being sent contains binary data - /// (e.g. one mor more MIME parts contains a Content-Transfer-Encoding: binary header). This - /// option provides a way to override this behavior, forcing the to send - /// messages using the BDAT command instead of the DATA command even when it is not - /// necessary to do so. - /// - /// true if the BDAT command is preferred over the DATA command; otherwise, false. - protected virtual bool PreferSendAsBinaryData { - get { return false; } - } - - /// - /// Get the maximum message size supported by the server. - /// - /// - /// The maximum message size will not be known until a successful connection has - /// been made and may change once the client is authenticated. - /// This value is only relevant if the includes - /// the flag. - /// - /// - /// - /// - /// The maximum message size supported by the server. - public uint MaxSize { - get; private set; - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (SmtpClient)); - } - - #region IMailService implementation - - /// - /// Get the authentication mechanisms supported by the SMTP server. - /// - /// - /// The authentication mechanisms are queried as part of the connection - /// process. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before authenticating. - /// - /// - /// - /// - /// The authentication mechanisms. - public override HashSet AuthenticationMechanisms { - get { return authenticationMechanisms; } - } - - /// - /// Get or set the timeout for network streaming operations, in milliseconds. - /// - /// - /// Gets or sets the underlying socket stream's - /// and values. - /// - /// The timeout in milliseconds. - public override int Timeout { - get { return timeout; } - set { - if (IsConnected && Stream.CanTimeout) { - Stream.WriteTimeout = value; - Stream.ReadTimeout = value; - } - - timeout = value; - } - } - - /// - /// Get whether or not the client is currently connected to an SMTP server. - /// - /// - /// The state is set to true immediately after - /// one of the Connect - /// methods succeeds and is not set back to false until either the client - /// is disconnected via or until an - /// is thrown while attempting to read or write to - /// the underlying network socket. - /// When an is caught, the connection state of the - /// should be checked before continuing. - /// - /// - /// - /// - /// true if the client is connected; otherwise, false. - public override bool IsConnected { - get { return connected; } - } - - /// - /// 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). - /// - /// true if the connection is secure; otherwise, false. - public override bool IsSecure { - get { return IsConnected && secure; } - } - - /// - /// Get whether or not the client is currently authenticated with the SMTP server. - /// - /// - /// Gets whether or not the client is currently authenticated with the SMTP server. - /// To authenticate with the SMTP server, use one of the - /// Authenticate - /// methods. - /// - /// true if the client is connected; otherwise, false. - public override bool IsAuthenticated { - get { return authenticated; } - } - - bool ValidateRemoteCertificate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { - if (ServerCertificateValidationCallback != null) - return ServerCertificateValidationCallback (uri.Host, certificate, chain, sslPolicyErrors); - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (ServicePointManager.ServerCertificateValidationCallback != null) - return ServicePointManager.ServerCertificateValidationCallback (uri.Host, certificate, chain, sslPolicyErrors); -#endif - - return DefaultServerCertificateValidationCallback (uri.Host, certificate, chain, sslPolicyErrors); - } - - async Task QueueCommandAsync (SmtpCommand type, string command, bool doAsync, CancellationToken cancellationToken) - { - var bytes = Encoding.UTF8.GetBytes (command + "\r\n"); - - // Note: queued commands will be buffered by the stream - if (doAsync) - await Stream.WriteAsync (bytes, 0, bytes.Length, cancellationToken).ConfigureAwait (false); - else - Stream.Write (bytes, 0, bytes.Length, cancellationToken); - queued.Add (type); - } - - /// - /// Invoked only when no recipients were accepted by the SMTP server. - /// - /// - /// If is overridden to not throw - /// an exception, this method should be overridden to throw an appropriate - /// exception instead. - /// - /// The message being sent. - protected virtual void OnNoRecipientsAccepted (MimeMessage message) - { - } - - async Task FlushCommandQueueAsync (MimeMessage message, MailboxAddress sender, IList recipients, bool doAsync, CancellationToken cancellationToken) - { - try { - // Note: Queued commands are buffered by the stream - if (doAsync) - await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); - else - Stream.Flush (cancellationToken); - } catch { - queued.Clear (); - throw; - } - - var responses = new List (); - Exception rex = null; - int accepted = 0; - int rcpt = 0; - - // 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. - try { - for (int i = 0; i < queued.Count; i++) { - SmtpResponse response; - - if (doAsync) - response = await Stream.ReadResponseAsync (cancellationToken).ConfigureAwait (false); - else - response = Stream.ReadResponse (cancellationToken); - - responses.Add (response); - } - } catch (Exception ex) { - // Note: Most likely this exception is due to an unexpected disconnect. - // Usually, before an SMTP server disconnects the client, it will send an - // error code response that will be more useful to the user than an error - // stating that the server has unexpected disconnected. Save this exception - // in case the server didn't give us a response with an error code. - rex = ex; - } - - try { - // process the responses - for (int i = 0; i < responses.Count; i++) { - switch (queued[i]) { - case SmtpCommand.MailFrom: - ProcessMailFromResponse (message, sender, responses[i]); - break; - case SmtpCommand.RcptTo: - if (ProcessRcptToResponse (message, recipients[rcpt++], responses[i])) - accepted++; - break; - } - } - } finally { - queued.Clear (); - } - - // throw the saved exception - if (rex != null) - throw rex; - - if (accepted == 0) - OnNoRecipientsAccepted (message); - } - - async Task SendCommandAsync (string command, bool doAsync, CancellationToken cancellationToken) - { - var bytes = Encoding.UTF8.GetBytes (command + "\r\n"); - - if (doAsync) { - await Stream.WriteAsync (bytes, 0, bytes.Length, cancellationToken).ConfigureAwait (false); - await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); - - return await Stream.ReadResponseAsync (cancellationToken).ConfigureAwait (false); - } - - Stream.Write (bytes, 0, bytes.Length, cancellationToken); - Stream.Flush (cancellationToken); - - return Stream.ReadResponse (cancellationToken); - } - - /// - /// Send a custom command to the SMTP server. - /// - /// - /// Sends a custom command to the SMTP server. - /// The command string should not include the terminating \r\n sequence. - /// - /// The command response. - /// The command. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - protected SmtpResponse SendCommand (string command, CancellationToken cancellationToken = default (CancellationToken)) - { - if (command == null) - throw new ArgumentNullException (nameof (command)); - - CheckDisposed (); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The SmtpClient must be connected before you can send commands."); - - return SendCommandAsync (command, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task SendEhloAsync (bool ehlo, bool doAsync, CancellationToken cancellationToken) - { - var network = NetworkStream.Get (Stream.Stream); - string command = ehlo ? "EHLO " : "HELO "; - string domain = null; - IPAddress ip = null; - - if (!string.IsNullOrEmpty (LocalDomain)) { - if (!IPAddress.TryParse (LocalDomain, out ip)) - domain = LocalDomain; - } else if (network != null) { - var ipEndPoint = network.Socket.LocalEndPoint as IPEndPoint; - - if (ipEndPoint == null) - domain = ((DnsEndPoint) network.Socket.LocalEndPoint).Host; - else - ip = ipEndPoint.Address; - } else { - domain = "[127.0.0.1]"; - } - - if (ip != null) { - if (ip.IsIPv4MappedToIPv6) { - try { - ip = ip.MapToIPv4 (); - } catch (ArgumentOutOfRangeException) { - // .NET 4.5.2 bug on Windows 7 SP1 (issue #814) - } - } - - if (ip.AddressFamily == AddressFamily.InterNetworkV6) - domain = "[IPv6:" + ip + "]"; - else - domain = "[" + ip + "]"; - } - - command += domain; - - return await SendCommandAsync (command, doAsync, cancellationToken).ConfigureAwait (false); - } - - async Task EhloAsync (bool doAsync, CancellationToken cancellationToken) - { - SmtpResponse response; - - response = await SendEhloAsync (true, doAsync, cancellationToken).ConfigureAwait (false); - - // Some SMTP servers do not accept an EHLO after authentication (despite the rfc saying it is required). - if (authenticated && response.StatusCode == SmtpStatusCode.BadCommandSequence) - return; - - if (response.StatusCode != SmtpStatusCode.Ok) { - // Try sending HELO instead... - response = await SendEhloAsync (false, doAsync, cancellationToken).ConfigureAwait (false); - if (response.StatusCode != SmtpStatusCode.Ok) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - } else { - // Clear the extensions except STARTTLS so that this capability stays set after a STARTTLS command. - capabilities &= SmtpCapabilities.StartTLS; - AuthenticationMechanisms.Clear (); - MaxSize = 0; - - var lines = response.Response.Split ('\n'); - for (int i = 0; i < lines.Length; i++) { - // Outlook.com replies with "250-8bitmime" instead of "250-8BITMIME" - // (strangely, it correctly capitalizes all other extensions...) - var capability = lines[i].Trim ().ToUpperInvariant (); - - if (capability.StartsWith ("AUTH", StringComparison.Ordinal) || capability.StartsWith ("X-EXPS", StringComparison.Ordinal)) { - int index = capability[0] == 'A' ? "AUTH".Length : "X-EXPS".Length; - - if (index < capability.Length && (capability[index] == ' ' || capability[index] == '=')) { - capabilities |= SmtpCapabilities.Authentication; - index++; - - var mechanisms = capability.Substring (index); - foreach (var mechanism in mechanisms.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) - AuthenticationMechanisms.Add (mechanism); - } - } else if (capability.StartsWith ("SIZE", StringComparison.Ordinal)) { - int index = 4; - uint size; - - capabilities |= SmtpCapabilities.Size; - - while (index < capability.Length && char.IsWhiteSpace (capability[index])) - index++; - - if (uint.TryParse (capability.Substring (index), NumberStyles.None, CultureInfo.InvariantCulture, out size)) - MaxSize = size; - } else if (capability == "DSN") { - capabilities |= SmtpCapabilities.Dsn; - } else if (capability == "BINARYMIME") { - capabilities |= SmtpCapabilities.BinaryMime; - } else if (capability == "CHUNKING") { - capabilities |= SmtpCapabilities.Chunking; - } else if (capability == "ENHANCEDSTATUSCODES") { - capabilities |= SmtpCapabilities.EnhancedStatusCodes; - } else if (capability == "8BITMIME") { - capabilities |= SmtpCapabilities.EightBitMime; - } else if (capability == "PIPELINING") { - capabilities |= SmtpCapabilities.Pipelining; - } else if (capability == "STARTTLS") { - capabilities |= SmtpCapabilities.StartTLS; - } else if (capability == "SMTPUTF8") { - capabilities |= SmtpCapabilities.UTF8; - } - } - } - } - - async Task AuthenticateAsync (SaslMechanism mechanism, bool doAsync, CancellationToken cancellationToken) - { - if (mechanism == null) - throw new ArgumentNullException (nameof (mechanism)); - - CheckDisposed (); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The SmtpClient must be connected before you can authenticate."); - - if (IsAuthenticated) - throw new InvalidOperationException ("The SmtpClient is already authenticated."); - - if ((capabilities & SmtpCapabilities.Authentication) == 0) - throw new NotSupportedException ("The SMTP server does not support authentication."); - - cancellationToken.ThrowIfCancellationRequested (); - - SmtpResponse response; - string challenge; - string command; - - mechanism.Uri = new Uri ($"smtp://{uri.Host}"); - - // send an initial challenge if the mechanism supports it - if (mechanism.SupportsInitialResponse) { - challenge = mechanism.Challenge (null); - command = string.Format ("AUTH {0} {1}", mechanism.MechanismName, challenge); - } else { - command = string.Format ("AUTH {0}", mechanism.MechanismName); - } - - response = await SendCommandAsync (command, doAsync, cancellationToken).ConfigureAwait (false); - - if (response.StatusCode == SmtpStatusCode.AuthenticationMechanismTooWeak) - throw new AuthenticationException (response.Response); - - SaslException saslException = null; - - try { - while (!mechanism.IsAuthenticated) { - if (response.StatusCode != SmtpStatusCode.AuthenticationChallenge) - break; - - challenge = mechanism.Challenge (response.Response); - response = await SendCommandAsync (challenge, doAsync, cancellationToken).ConfigureAwait (false); - } - - saslException = null; - } catch (SaslException ex) { - // reset the authentication state - response = await SendCommandAsync (string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - saslException = ex; - } - - if (response.StatusCode == SmtpStatusCode.AuthenticationSuccessful) { - if (mechanism.NegotiatedSecurityLayer) - await EhloAsync (doAsync, cancellationToken).ConfigureAwait (false); - authenticated = true; - OnAuthenticated (response.Response); - return; - } - - var message = string.Format (CultureInfo.InvariantCulture, "{0}: {1}", (int) response.StatusCode, response.Response); - - if (saslException != null) - throw new AuthenticationException (message, saslException); - - throw new AuthenticationException (message); - } - - /// - /// Authenticate using the specified SASL mechanism. - /// - /// - /// Authenticates using the specified SASL mechanism. - /// For a list of available SASL authentication mechanisms supported by the server, - /// check the property after the service has been - /// connected. - /// - /// The SASL mechanism. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The SMTP server does not support authentication. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override void Authenticate (SaslMechanism mechanism, CancellationToken cancellationToken = default (CancellationToken)) - { - AuthenticateAsync (mechanism, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task AuthenticateAsync (Encoding encoding, ICredentials credentials, bool doAsync, CancellationToken cancellationToken) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (credentials == null) - throw new ArgumentNullException (nameof (credentials)); - - CheckDisposed (); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The SmtpClient must be connected before you can authenticate."); - - if (IsAuthenticated) - throw new InvalidOperationException ("The SmtpClient is already authenticated."); - - if ((capabilities & SmtpCapabilities.Authentication) == 0) - throw new NotSupportedException ("The SMTP server does not support authentication."); - - var saslUri = new Uri ($"smtp://{uri.Host}"); - AuthenticationException authException = null; - SmtpResponse response; - SaslMechanism sasl; - bool tried = false; - string challenge; - string command; - - foreach (var authmech in SaslMechanism.AuthMechanismRank) { - if (!AuthenticationMechanisms.Contains (authmech)) - continue; - - if ((sasl = SaslMechanism.Create (authmech, saslUri, encoding, credentials)) == null) - continue; - - tried = true; - - cancellationToken.ThrowIfCancellationRequested (); - - // send an initial challenge if the mechanism supports it - if (sasl.SupportsInitialResponse) { - challenge = sasl.Challenge (null); - command = string.Format ("AUTH {0} {1}", authmech, challenge); - } else { - command = string.Format ("AUTH {0}", authmech); - } - - response = await SendCommandAsync (command, doAsync, cancellationToken).ConfigureAwait (false); - - if (response.StatusCode == SmtpStatusCode.AuthenticationMechanismTooWeak) - continue; - - SaslException saslException = null; - - try { - while (!sasl.IsAuthenticated) { - if (response.StatusCode != SmtpStatusCode.AuthenticationChallenge) - break; - - challenge = sasl.Challenge (response.Response); - response = await SendCommandAsync (challenge, doAsync, cancellationToken).ConfigureAwait (false); - } - - saslException = null; - } catch (SaslException ex) { - // reset the authentication state - response = await SendCommandAsync (string.Empty, doAsync, cancellationToken).ConfigureAwait (false); - saslException = ex; - } - - if (response.StatusCode == SmtpStatusCode.AuthenticationSuccessful) { - if (sasl.NegotiatedSecurityLayer) - await EhloAsync (doAsync, cancellationToken).ConfigureAwait (false); - authenticated = true; - OnAuthenticated (response.Response); - return; - } - - var message = string.Format (CultureInfo.InvariantCulture, "{0}: {1}", (int) response.StatusCode, response.Response); - Exception inner; - - if (saslException != null) - inner = new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response, saslException); - else - inner = new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - authException = new AuthenticationException (message, inner); - } - - if (tried) - throw authException ?? new AuthenticationException (); - - throw new NotSupportedException ("No compatible authentication mechanisms found."); - } - - /// - /// Authenticate using the supplied credentials. - /// - /// - /// If the SMTP server supports authentication, then the SASL mechanisms - /// that both the client and server support are tried in order of greatest - /// security to weakest security. Once a SASL authentication mechanism is - /// found that both client and server support, the credentials are used to - /// authenticate. - /// If, on the other hand, authentication is not supported by the SMTP - /// server, then this method will throw . - /// The property can be checked for the - /// flag to make sure the - /// SMTP server supports authentication before calling this method. - /// To prevent the usage of certain authentication mechanisms, - /// simply remove them from the hash set - /// before calling this method. - /// - /// The text encoding to use for the user's credentials. - /// The user's credentials. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The is not connected. - /// - /// - /// The is already authenticated. - /// - /// - /// The SMTP server does not support authentication. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// Authentication using the supplied credentials has failed. - /// - /// - /// A SASL authentication error occurred. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override void Authenticate (Encoding encoding, ICredentials credentials, CancellationToken cancellationToken = default (CancellationToken)) - { - AuthenticateAsync (encoding, credentials, false, cancellationToken).GetAwaiter ().GetResult (); - } - - internal void ReplayConnect (string host, Stream replayStream, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (replayStream == null) - throw new ArgumentNullException (nameof (replayStream)); - - Stream = new SmtpStream (replayStream, ProtocolLogger); - capabilities = SmtpCapabilities.None; - AuthenticationMechanisms.Clear (); - uri = new Uri ($"smtp://{host}:25"); - secure = false; - MaxSize = 0; - - try { - // read the greeting - var response = Stream.ReadResponse (cancellationToken); - - if (response.StatusCode != SmtpStatusCode.ServiceReady) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - // Send EHLO and get a list of supported extensions - EhloAsync (false, cancellationToken).GetAwaiter ().GetResult (); - - connected = true; - } catch { - Stream.Dispose (); - Stream = null; - throw; - } - - OnConnected (host, 25, SecureSocketOptions.None); - } - - internal async Task ReplayConnectAsync (string host, Stream replayStream, CancellationToken cancellationToken = default (CancellationToken)) - { - CheckDisposed (); - - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (replayStream == null) - throw new ArgumentNullException (nameof (replayStream)); - - Stream = new SmtpStream (replayStream, ProtocolLogger); - capabilities = SmtpCapabilities.None; - AuthenticationMechanisms.Clear (); - uri = new Uri ($"smtp://{host}:25"); - secure = false; - MaxSize = 0; - - try { - // read the greeting - var response = await Stream.ReadResponseAsync (cancellationToken).ConfigureAwait (false); - - if (response.StatusCode != SmtpStatusCode.ServiceReady) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - // Send EHLO and get a list of supported extensions - await EhloAsync (true, cancellationToken).ConfigureAwait (false); - - connected = true; - } catch { - Stream.Dispose (); - Stream = null; - throw; - } - - OnConnected (host, 25, SecureSocketOptions.None); - } - - internal static void ComputeDefaultValues (string host, ref int port, ref SecureSocketOptions options, out Uri uri, out bool starttls) - { - switch (options) { - default: - if (port == 0) - port = 25; - break; - case SecureSocketOptions.Auto: - switch (port) { - case 0: port = 25; goto default; - case 465: options = SecureSocketOptions.SslOnConnect; break; - default: options = SecureSocketOptions.StartTlsWhenAvailable; break; - } - break; - case SecureSocketOptions.SslOnConnect: - if (port == 0) - port = 465; - break; - } - - switch (options) { - case SecureSocketOptions.StartTlsWhenAvailable: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "smtp://{0}:{1}/?starttls=when-available", host, port)); - starttls = true; - break; - case SecureSocketOptions.StartTls: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "smtp://{0}:{1}/?starttls=always", host, port)); - starttls = true; - break; - case SecureSocketOptions.SslOnConnect: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "smtps://{0}:{1}", host, port)); - starttls = false; - break; - default: - uri = new Uri (string.Format (CultureInfo.InvariantCulture, "smtp://{0}:{1}", host, port)); - starttls = false; - break; - } - } - - async Task ConnectAsync (string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0) - throw new ArgumentException ("The host name cannot be empty.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - CheckDisposed (); - - if (IsConnected) - throw new InvalidOperationException ("The SmtpClient is already connected."); - - capabilities = SmtpCapabilities.None; - AuthenticationMechanisms.Clear (); - MaxSize = 0; - - SmtpResponse response; - Stream stream; - bool starttls; - - ComputeDefaultValues (host, ref port, ref options, out uri, out starttls); - - var socket = await ConnectSocket (host, port, doAsync, cancellationToken).ConfigureAwait (false); - - if (options == SecureSocketOptions.SslOnConnect) { - var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); - - try { - if (doAsync) { - await ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - ssl.Dispose (); - - throw SslHandshakeException.Create (this, ex, false); - } - - secure = true; - stream = ssl; - } else { - stream = new NetworkStream (socket, true); - secure = false; - } - - if (stream.CanTimeout) { - stream.WriteTimeout = timeout; - stream.ReadTimeout = timeout; - } - - Stream = new SmtpStream (stream, ProtocolLogger); - - try { - ProtocolLogger.LogConnect (uri); - - // read the greeting - if (doAsync) - response = await Stream.ReadResponseAsync (cancellationToken).ConfigureAwait (false); - else - response = Stream.ReadResponse (cancellationToken); - - if (response.StatusCode != SmtpStatusCode.ServiceReady) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - // Send EHLO and get a list of supported extensions - await EhloAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (options == SecureSocketOptions.StartTls && (capabilities & SmtpCapabilities.StartTLS) == 0) - throw new NotSupportedException ("The SMTP server does not support the STARTTLS extension."); - - if (starttls && (capabilities & SmtpCapabilities.StartTLS) != 0) { - response = await SendCommandAsync ("STARTTLS", doAsync, cancellationToken).ConfigureAwait (false); - if (response.StatusCode != SmtpStatusCode.ServiceReady) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - try { - var tls = new SslStream (stream, false, ValidateRemoteCertificate); - Stream.Stream = tls; - - if (doAsync) { - await tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - throw SslHandshakeException.Create (this, ex, true); - } - - secure = true; - - // Send EHLO again and get the new list of supported extensions - await EhloAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - connected = true; - } catch { - Stream.Dispose (); - secure = false; - Stream = null; - throw; - } - - OnConnected (host, port, options); - } - - /// - /// Establish a connection to the specified SMTP or SMTP/S server. - /// - /// - /// Establishes a connection to the specified SMTP or SMTP/S server. - /// If the has a value of 0, then the - /// parameter is used to determine the default port to - /// connect to. The default port used with - /// is 465. All other values will use a default port of 25. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 465, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// The connection established by any of the - /// Connect - /// methods may be re-used if an application wishes to send multiple messages - /// to the same SMTP server. Since connecting and authenticating can be expensive - /// operations, re-using a connection can significantly improve performance when - /// sending a large number of messages to the same SMTP server over a short - /// period of time. - /// - /// - /// - /// - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the SMTP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled. - /// - /// - /// A socket error occurred trying to connect to the remote host. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task ConnectAsync (Stream stream, string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (host == null) - throw new ArgumentNullException (nameof (host)); - - if (host.Length == 0) - throw new ArgumentException ("The host name cannot be empty.", nameof (host)); - - if (port < 0 || port > 65535) - throw new ArgumentOutOfRangeException (nameof (port)); - - CheckDisposed (); - - if (IsConnected) - throw new InvalidOperationException ("The SmtpClient is already connected."); - - capabilities = SmtpCapabilities.None; - AuthenticationMechanisms.Clear (); - MaxSize = 0; - - SmtpResponse response; - Stream network; - bool starttls; - - ComputeDefaultValues (host, ref port, ref options, out uri, out starttls); - - if (options == SecureSocketOptions.SslOnConnect) { - var ssl = new SslStream (stream, false, ValidateRemoteCertificate); - - try { - if (doAsync) { - await ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - ssl.Dispose (); - - throw SslHandshakeException.Create (this, ex, false); - } - - network = ssl; - secure = true; - } else { - network = stream; - secure = false; - } - - if (network.CanTimeout) { - network.WriteTimeout = timeout; - network.ReadTimeout = timeout; - } - - Stream = new SmtpStream (network, ProtocolLogger); - - try { - ProtocolLogger.LogConnect (uri); - - // read the greeting - if (doAsync) - response = await Stream.ReadResponseAsync (cancellationToken).ConfigureAwait (false); - else - response = Stream.ReadResponse (cancellationToken); - - if (response.StatusCode != SmtpStatusCode.ServiceReady) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - // Send EHLO and get a list of supported extensions - await EhloAsync (doAsync, cancellationToken).ConfigureAwait (false); - - if (options == SecureSocketOptions.StartTls && (capabilities & SmtpCapabilities.StartTLS) == 0) - throw new NotSupportedException ("The SMTP server does not support the STARTTLS extension."); - - if (starttls && (capabilities & SmtpCapabilities.StartTLS) != 0) { - response = await SendCommandAsync ("STARTTLS", doAsync, cancellationToken).ConfigureAwait (false); - if (response.StatusCode != SmtpStatusCode.ServiceReady) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - var tls = new SslStream (network, false, ValidateRemoteCertificate); - Stream.Stream = tls; - - try { - if (doAsync) { - await tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait (false); - } else { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter ().GetResult (); -#else - tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, CheckCertificateRevocation); -#endif - } - } catch (Exception ex) { - throw SslHandshakeException.Create (this, ex, true); - } - - secure = true; - - // Send EHLO again and get the new list of supported extensions - await EhloAsync (doAsync, cancellationToken).ConfigureAwait (false); - } - - connected = true; - } catch { - Stream.Dispose (); - secure = false; - Stream = null; - throw; - } - - OnConnected (host, port, options); - } - - Task ConnectAsync (Socket socket, string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken) - { - if (socket == null) - throw new ArgumentNullException (nameof (socket)); - - if (!socket.Connected) - throw new ArgumentException ("The socket is not connected.", nameof (socket)); - - return ConnectAsync (new NetworkStream (socket, true), host, port, options, doAsync, cancellationToken); - } - - /// - /// Establish a connection to the specified SMTP or SMTP/S server using the provided socket. - /// - /// - /// Establishes a connection to the specified SMTP or SMTP/S server using the provided socket. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 465, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// The socket to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// is not connected. - /// -or- - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the SMTP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override void Connect (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (socket, host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Establish a connection to the specified SMTP or SMTP/S server using the provided stream. - /// - /// - /// Establishes a connection to the specified SMTP or SMTP/S server using the provided stream. - /// If the has a value of - /// , then the is used - /// to determine the default security options. If the has a value - /// of 465, then the default options used will be - /// . All other values will use - /// . - /// Once a connection is established, properties such as - /// and will be - /// populated. - /// With the exception of using the to determine the - /// default to use when the value - /// is , the and - /// parameters are only used for logging purposes. - /// - /// The stream to use for the connection. - /// The host name to connect to. - /// The port to connect to. If the specified port is 0, then the default port will be used. - /// The secure socket options to when connecting. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not between 0 and 65535. - /// - /// - /// The is a zero-length string. - /// - /// - /// The has been disposed. - /// - /// - /// The is already connected. - /// - /// - /// was set to - /// - /// and the SMTP server does not support the STARTTLS extension. - /// - /// - /// The operation was canceled. - /// - /// - /// An error occurred during the SSL/TLS negotiations. - /// - /// - /// An I/O error occurred. - /// - /// - /// An SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override void Connect (Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken)) - { - ConnectAsync (stream, host, port, options, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task DisconnectAsync (bool quit, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (!IsConnected) - return; - - if (quit) { - try { - await SendCommandAsync ("QUIT", doAsync, cancellationToken).ConfigureAwait (false); - } catch (OperationCanceledException) { - } catch (SmtpProtocolException) { - } catch (SmtpCommandException) { - } catch (IOException) { - } - } - - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), true); - } - - /// - /// Disconnect the service. - /// - /// - /// If is true, a QUIT command will be issued in order to disconnect cleanly. - /// - /// - /// - /// - /// If set to true, a QUIT command will be issued in order to disconnect cleanly. - /// The cancellation token. - /// - /// The has been disposed. - /// - public override void Disconnect (bool quit, CancellationToken cancellationToken = default (CancellationToken)) - { - DisconnectAsync (quit, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task NoOpAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The SmtpClient is not connected."); - - SmtpResponse response; - - try { - response = await SendCommandAsync ("NOOP", doAsync, cancellationToken).ConfigureAwait (false); - } catch { - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); - throw; - } - - if (response.StatusCode != SmtpStatusCode.Ok) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - } - - /// - /// Ping the SMTP server to keep the connection alive. - /// - /// Mail servers, if left idle for too long, will automatically drop the connection. - /// The cancellation token. - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// The operation was canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol error occurred. - /// - public override void NoOp (CancellationToken cancellationToken = default (CancellationToken)) - { - NoOpAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - void Disconnect (string host, int port, SecureSocketOptions options, bool requested) - { - capabilities = SmtpCapabilities.None; - authenticated = false; - connected = false; - secure = false; - uri = null; - - if (Stream != null) { - Stream.Dispose (); - Stream = null; - } - - if (host != null) - OnDisconnected (host, port, options, requested); - } - - #endregion - - #region IMailTransport implementation - - static MailboxAddress GetMessageSender (MimeMessage message) - { - if (message.ResentSender != null) - return message.ResentSender; - - if (message.ResentFrom.Count > 0) - return message.ResentFrom.Mailboxes.FirstOrDefault (); - - if (message.Sender != null) - return message.Sender; - - return message.From.Mailboxes.FirstOrDefault (); - } - - static void AddUnique (IList recipients, HashSet unique, IEnumerable mailboxes) - { - foreach (var mailbox in mailboxes) { - if (unique.Add (mailbox.Address)) - recipients.Add (mailbox); - } - } - - static IList GetMessageRecipients (MimeMessage message) - { - var unique = new HashSet (StringComparer.OrdinalIgnoreCase); - var recipients = new List (); - - if (message.ResentSender != null || message.ResentFrom.Count > 0) { - AddUnique (recipients, unique, message.ResentTo.Mailboxes); - AddUnique (recipients, unique, message.ResentCc.Mailboxes); - AddUnique (recipients, unique, message.ResentBcc.Mailboxes); - } else { - AddUnique (recipients, unique, message.To.Mailboxes); - AddUnique (recipients, unique, message.Cc.Mailboxes); - AddUnique (recipients, unique, message.Bcc.Mailboxes); - } - - return recipients; - } - - [Flags] - enum SmtpExtension { - None = 0, - EightBitMime = 1 << 0, - BinaryMime = 1 << 1, - UTF8 = 1 << 2, - } - - class ContentTransferEncodingVisitor : MimeVisitor - { - readonly SmtpCapabilities capabilities; - - public ContentTransferEncodingVisitor (SmtpCapabilities capabilities) - { - this.capabilities = capabilities; - } - - public SmtpExtension SmtpExtensions { - get; private set; - } - - protected override void VisitMimePart (MimePart entity) - { - switch (entity.ContentTransferEncoding) { - case ContentEncoding.EightBit: - if ((capabilities & SmtpCapabilities.EightBitMime) != 0) - SmtpExtensions |= SmtpExtension.EightBitMime; - break; - case ContentEncoding.Binary: - if ((capabilities & SmtpCapabilities.BinaryMime) != 0) - SmtpExtensions |= SmtpExtension.BinaryMime; - break; - } - } - } - - /// - /// Invoked when the sender is accepted by the SMTP server. - /// - /// - /// The default implementation does nothing. - /// - /// The message being sent. - /// The mailbox used in the MAIL FROM command. - /// The response to the MAIL FROM command. - protected virtual void OnSenderAccepted (MimeMessage message, MailboxAddress mailbox, SmtpResponse response) - { - } - - /// - /// Invoked when a recipient is not accepted by the SMTP server. - /// - /// - /// The default implementation throws an appropriate . - /// - /// The message being sent. - /// The mailbox used in the MAIL FROM command. - /// The response to the MAIL FROM command. - protected virtual void OnSenderNotAccepted (MimeMessage message, MailboxAddress mailbox, SmtpResponse response) - { - throw new SmtpCommandException (SmtpErrorCode.SenderNotAccepted, response.StatusCode, mailbox, response.Response); - } - - void ProcessMailFromResponse (MimeMessage message, MailboxAddress mailbox, SmtpResponse response) - { - if (response.StatusCode >= SmtpStatusCode.Ok && response.StatusCode < (SmtpStatusCode) 260) { - OnSenderAccepted (message, mailbox, response); - return; - } - - if (response.StatusCode == SmtpStatusCode.AuthenticationRequired) - throw new ServiceNotAuthenticatedException (response.Response); - - OnSenderNotAccepted (message, mailbox, response); - } - - /// - /// Get the envelope identifier to be used with delivery status notifications. - /// - /// - /// The envelope identifier, if non-empty, is useful in determining which message a delivery - /// status notification was issued for. - /// The envelope identifier should be unique and may be up to 100 characters in length, but - /// must consist only of printable ASCII characters and no white space. - /// For more information, see - /// rfc3461, section 4.4. - /// - /// - /// - /// - /// The envelope identifier. - /// The message. - protected virtual string GetEnvelopeId (MimeMessage message) - { - return null; - } - - /// - /// Get or set how much of the message to include in any failed delivery status notifications. - /// - /// - /// Gets or sets how much of the message to include in any failed delivery status notifications. - /// - /// A value indicating how much of the message to include in a failure delivery status notification. - public DeliveryStatusNotificationType DeliveryStatusNotificationType { - get; set; - } - - async Task MailFromAsync (FormatOptions options, MimeMessage message, MailboxAddress mailbox, SmtpExtension extensions, long size, bool doAsync, CancellationToken cancellationToken) - { - var idnEncode = (extensions & SmtpExtension.UTF8) == 0; - var builder = new StringBuilder ("MAIL FROM:<"); - - var addrspec = mailbox.GetAddress (idnEncode); - builder.Append (addrspec); - builder.Append ('>'); - - if (!idnEncode) - builder.Append (" SMTPUTF8"); - - if ((Capabilities & SmtpCapabilities.Size) != 0 && size != -1) - builder.AppendFormat (CultureInfo.InvariantCulture, " SIZE={0}", size); - - if ((extensions & SmtpExtension.BinaryMime) != 0) - builder.Append (" BODY=BINARYMIME"); - else if ((extensions & SmtpExtension.EightBitMime) != 0) - builder.Append (" BODY=8BITMIME"); - - if ((capabilities & SmtpCapabilities.Dsn) != 0) { - var envid = GetEnvelopeId (message); - - if (!string.IsNullOrEmpty (envid)) { - builder.Append (" ENVID="); - builder.Append (envid); - } - - switch (DeliveryStatusNotificationType) { - case DeliveryStatusNotificationType.HeadersOnly: - builder.Append (" RET=HDRS"); - break; - case DeliveryStatusNotificationType.Full: - builder.Append (" RET=FULL"); - break; - } - } - - var command = builder.ToString (); - - if ((capabilities & SmtpCapabilities.Pipelining) != 0) { - await QueueCommandAsync (SmtpCommand.MailFrom, command, doAsync, cancellationToken).ConfigureAwait (false); - return; - } - - var response = await SendCommandAsync (command, doAsync, cancellationToken).ConfigureAwait (false); - - ProcessMailFromResponse (message, mailbox, response); - } - - /// - /// Invoked when a recipient is accepted by the SMTP server. - /// - /// - /// The default implementation does nothing. - /// - /// The message being sent. - /// The mailbox used in the RCPT TO command. - /// The response to the RCPT TO command. - protected virtual void OnRecipientAccepted (MimeMessage message, MailboxAddress mailbox, SmtpResponse response) - { - } - - /// - /// Invoked when a recipient is not accepted by the SMTP server. - /// - /// - /// The default implementation throws an appropriate . - /// - /// The message being sent. - /// The mailbox used in the RCPT TO command. - /// The response to the RCPT TO command. - protected virtual void OnRecipientNotAccepted (MimeMessage message, MailboxAddress mailbox, SmtpResponse response) - { - throw new SmtpCommandException (SmtpErrorCode.RecipientNotAccepted, response.StatusCode, mailbox, response.Response); - } - - bool ProcessRcptToResponse (MimeMessage message, MailboxAddress mailbox, SmtpResponse response) - { - if (response.StatusCode < (SmtpStatusCode) 300) { - OnRecipientAccepted (message, mailbox, response); - return true; - } - - if (response.StatusCode == SmtpStatusCode.AuthenticationRequired) - throw new ServiceNotAuthenticatedException (response.Response); - - OnRecipientNotAccepted (message, mailbox, response); - - return false; - } - - /// - /// Get the types of delivery status notification desired for the specified recipient mailbox. - /// - /// - /// Gets the types of delivery status notification desired for the specified recipient mailbox. - /// - /// - /// - /// - /// The desired delivery status notification type. - /// The message being sent. - /// The mailbox. - protected virtual DeliveryStatusNotification? GetDeliveryStatusNotifications (MimeMessage message, MailboxAddress mailbox) - { - return null; - } - - static string GetNotifyString (DeliveryStatusNotification notify) - { - string value = string.Empty; - - if (notify == DeliveryStatusNotification.Never) - return "NEVER"; - - if ((notify & DeliveryStatusNotification.Success) != 0) - value += "SUCCESS,"; - - if ((notify & DeliveryStatusNotification.Failure) != 0) - value += "FAILURE,"; - - if ((notify & DeliveryStatusNotification.Delay) != 0) - value += "DELAY"; - - return value.TrimEnd (','); - } - - async Task RcptToAsync (FormatOptions options, MimeMessage message, MailboxAddress mailbox, bool doAsync, CancellationToken cancellationToken) - { - var idnEncode = (Capabilities & SmtpCapabilities.UTF8) == 0; - var command = string.Format ("RCPT TO:<{0}>", mailbox.GetAddress (idnEncode)); - - if ((capabilities & SmtpCapabilities.Dsn) != 0) { - var notify = GetDeliveryStatusNotifications (message, mailbox); - - if (notify.HasValue) - command += " NOTIFY=" + GetNotifyString (notify.Value); - } - - if ((capabilities & SmtpCapabilities.Pipelining) != 0) { - await QueueCommandAsync (SmtpCommand.RcptTo, command, doAsync, cancellationToken).ConfigureAwait (false); - return false; - } - - var response = await SendCommandAsync (command, doAsync, cancellationToken).ConfigureAwait (false); - - return ProcessRcptToResponse (message, mailbox, response); - } - - class SendContext - { - readonly ITransferProgress progress; - readonly long size; - long nwritten; - - public SendContext (ITransferProgress progress, long size) - { - this.progress = progress; - this.size = size; - } - - public void Update (int n) - { - nwritten += n; - - if (size != -1) - progress.Report (nwritten, size); - else - progress.Report (nwritten); - } - } - - async Task BdatAsync (FormatOptions options, MimeMessage message, long size, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - SmtpResponse response; - byte[] bytes; - - bytes = Encoding.UTF8.GetBytes (string.Format (CultureInfo.InvariantCulture, "BDAT {0} LAST\r\n", size)); - - if (doAsync) - await Stream.WriteAsync (bytes, 0, bytes.Length, cancellationToken).ConfigureAwait (false); - else - Stream.Write (bytes, 0, bytes.Length, cancellationToken); - - if (progress != null) { - var ctx = new SendContext (progress, size); - - using (var stream = new ProgressStream (Stream, ctx.Update)) { - if (doAsync) { - await message.WriteToAsync (options, stream, cancellationToken).ConfigureAwait (false); - await stream.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - message.WriteTo (options, stream, cancellationToken); - stream.Flush (cancellationToken); - } - } - } else if (doAsync) { - await message.WriteToAsync (options, Stream, cancellationToken).ConfigureAwait (false); - await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - message.WriteTo (options, Stream, cancellationToken); - Stream.Flush (cancellationToken); - } - - if (doAsync) - response = await Stream.ReadResponseAsync (cancellationToken).ConfigureAwait (false); - else - response = Stream.ReadResponse (cancellationToken); - - switch (response.StatusCode) { - default: - throw new SmtpCommandException (SmtpErrorCode.MessageNotAccepted, response.StatusCode, response.Response); - case SmtpStatusCode.AuthenticationRequired: - throw new ServiceNotAuthenticatedException (response.Response); - case SmtpStatusCode.Ok: - OnMessageSent (new MessageSentEventArgs (message, response.Response)); - break; - } - } - - async Task DataAsync (FormatOptions options, MimeMessage message, long size, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - var response = await SendCommandAsync ("DATA", doAsync, cancellationToken).ConfigureAwait (false); - - if (response.StatusCode != SmtpStatusCode.StartMailInput) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - if (progress != null) { - var ctx = new SendContext (progress, size); - - using (var stream = new ProgressStream (Stream, ctx.Update)) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (new SmtpDataFilter ()); - - if (doAsync) { - await message.WriteToAsync (options, filtered, cancellationToken).ConfigureAwait (false); - await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - message.WriteTo (options, filtered, cancellationToken); - filtered.Flush (cancellationToken); - } - } - } - } else { - using (var filtered = new FilteredStream (Stream)) { - filtered.Add (new SmtpDataFilter ()); - - if (doAsync) { - await message.WriteToAsync (options, filtered, cancellationToken).ConfigureAwait (false); - await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - message.WriteTo (options, filtered, cancellationToken); - filtered.Flush (cancellationToken); - } - } - } - - if (doAsync) { - await Stream.WriteAsync (EndData, 0, EndData.Length, cancellationToken).ConfigureAwait (false); - await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); - - response = await Stream.ReadResponseAsync (cancellationToken).ConfigureAwait (false); - } else { - Stream.Write (EndData, 0, EndData.Length, cancellationToken); - Stream.Flush (cancellationToken); - - response = Stream.ReadResponse (cancellationToken); - } - - switch (response.StatusCode) { - default: - throw new SmtpCommandException (SmtpErrorCode.MessageNotAccepted, response.StatusCode, response.Response); - case SmtpStatusCode.AuthenticationRequired: - throw new ServiceNotAuthenticatedException (response.Response); - case SmtpStatusCode.Ok: - OnMessageSent (new MessageSentEventArgs (message, response.Response)); - break; - } - } - - async Task ResetAsync (bool doAsync, CancellationToken cancellationToken) - { - try { - var response = await SendCommandAsync ("RSET", doAsync, cancellationToken).ConfigureAwait (false); - if (response.StatusCode != SmtpStatusCode.Ok) - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); - } catch (SmtpCommandException) { - // do not disconnect - } catch { - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri), false); - } - } - - /// - /// Prepare the message for transport with the specified constraints. - /// - /// - /// Prepares the message for transport with the specified constraints. - /// Typically, this involves calling on - /// the message with the provided constraints. - /// - /// The format options. - /// The message. - /// The encoding constraint. - /// The max line length supported by the server. - protected virtual void Prepare (FormatOptions options, MimeMessage message, EncodingConstraint constraint, int maxLineLength) - { - if (!message.Headers.Contains (HeaderId.DomainKeySignature) && - !message.Headers.Contains (HeaderId.DkimSignature) && - !message.Headers.Contains (HeaderId.ArcSeal)) { - // prepare the message - message.Prepare (constraint, maxLineLength); - } else { - // Note: we do not want to risk reformatting of headers to the international - // UTF-8 encoding, so disable it. - options.International = false; - } - } - - static async Task GetSizeAsync (FormatOptions options, MimeMessage message, bool doAsync, CancellationToken cancellationToken) - { - using (var measure = new MeasuringStream ()) { - if (doAsync) - await message.WriteToAsync (options, measure, cancellationToken).ConfigureAwait (false); - else - message.WriteTo (options, measure, cancellationToken); - - return measure.Length; - } - } - - /// - /// Get the size of the message. - /// - /// - /// Calculates the size of the message in bytes. - /// This method is called by Send - /// methods in the following conditions: - /// - /// The SMTP server supports the SIZE= parameter in the MAIL FROM command. - /// The parameter is non-null. - /// The SMTP server supports the CHUNKING extension. - /// - /// - /// The size of the message, in bytes. - /// The formatting options. - /// The message. - /// The cancellation token. - protected virtual long GetSize (FormatOptions options, MimeMessage message, CancellationToken cancellationToken) - { - return GetSizeAsync (options, message, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously get the size of the message. - /// - /// - /// Asynchronously calculates the size of the message in bytes. - /// This method is called by SendAsync - /// methods in the following conditions: - /// - /// The SMTP server supports the SIZE= parameter in the MAIL FROM command. - /// The parameter is non-null. - /// The SMTP server supports the CHUNKING extension. - /// - /// - /// The size of the message, in bytes. - /// The formatting options. - /// The message. - /// The cancellation token. - protected virtual Task GetSizeAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken) - { - return GetSizeAsync (options, message, true, cancellationToken); - } - - async Task SendAsync (FormatOptions options, MimeMessage message, MailboxAddress sender, IList recipients, bool doAsync, CancellationToken cancellationToken, ITransferProgress progress) - { - CheckDisposed (); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The SmtpClient is not connected."); - - var format = options.Clone (); - format.NewLineFormat = NewLineFormat.Dos; - format.EnsureNewLine = true; - - if (format.International && (Capabilities & SmtpCapabilities.UTF8) == 0) - format.International = false; - - if (format.International && (Capabilities & SmtpCapabilities.EightBitMime) == 0) - throw new NotSupportedException ("The SMTP server does not support the 8BITMIME extension."); - - EncodingConstraint constraint; - long size; - - if ((Capabilities & SmtpCapabilities.BinaryMime) != 0) - constraint = EncodingConstraint.None; - else if ((Capabilities & SmtpCapabilities.EightBitMime) != 0) - constraint = EncodingConstraint.EightBit; - else - constraint = EncodingConstraint.SevenBit; - - Prepare (format, message, constraint, MaxLineLength); - - // figure out which SMTP extensions we need to use - var visitor = new ContentTransferEncodingVisitor (capabilities); - visitor.Visit (message); - - var extensions = visitor.SmtpExtensions; - - if ((Capabilities & SmtpCapabilities.UTF8) != 0 && (format.International || sender.IsInternational || recipients.Any (x => x.IsInternational))) - extensions |= SmtpExtension.UTF8; - - if ((Capabilities & (SmtpCapabilities.Chunking | SmtpCapabilities.Size)) != 0 || progress != null) { - if (doAsync) - size = await GetSizeAsync (format, message, cancellationToken); - else - size = GetSize (format, message, cancellationToken); - } else { - size = -1; - } - - try { - // Note: if PIPELINING is supported, MailFrom() and RcptTo() will - // queue their commands instead of sending them immediately. - await MailFromAsync (format, message, sender, extensions, size, doAsync, cancellationToken).ConfigureAwait (false); - - int accepted = 0; - for (int i = 0; i < recipients.Count; i++) { - if (await RcptToAsync (format, message, recipients[i], doAsync, cancellationToken).ConfigureAwait (false)) - accepted++; - } - - if (queued.Count > 0) { - // Note: if PIPELINING is supported, this will flush all outstanding - // MAIL FROM and RCPT TO commands to the server and then process all - // of their responses. - await FlushCommandQueueAsync (message, sender, recipients, doAsync, cancellationToken).ConfigureAwait (false); - } else if (accepted == 0) { - OnNoRecipientsAccepted (message); - } - - if ((extensions & SmtpExtension.BinaryMime) != 0 || (PreferSendAsBinaryData && (Capabilities & SmtpCapabilities.BinaryMime) != 0)) - await BdatAsync (format, message, size, doAsync, cancellationToken, progress).ConfigureAwait (false); - else - await DataAsync (format, message, size, doAsync, cancellationToken, progress).ConfigureAwait (false); - } catch (ServiceNotAuthenticatedException) { - // do not disconnect - throw; - } catch (SmtpCommandException) { - await ResetAsync (doAsync, cancellationToken).ConfigureAwait (false); - throw; - } catch { - Disconnect (uri.Host, uri.Port, GetSecureSocketOptions (uri) , false); - throw; - } - } - - /// - /// Send the specified message. - /// - /// - /// Sends the specified message. - /// The sender address is determined by checking the following - /// message headers (in order of precedence): Resent-Sender, - /// Resent-From, Sender, and From. - /// If either the Resent-Sender or Resent-From addresses are present, - /// the recipients are collected from the Resent-To, Resent-Cc, and - /// Resent-Bcc headers, otherwise the To, Cc, and Bcc headers are used. - /// - /// - /// - /// - /// The formatting options. - /// The message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - public override void Send (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - var recipients = GetMessageRecipients (message); - var sender = GetMessageSender (message); - - if (sender == null) - throw new InvalidOperationException ("No sender has been specified."); - - if (recipients.Count == 0) - throw new InvalidOperationException ("No recipients have been specified."); - - SendAsync (options, message, sender, recipients, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - /// - /// Send the specified message using the supplied sender and recipients. - /// - /// - /// Sends the message by uploading it to an SMTP server using the supplied sender and recipients. - /// - /// The formatting options. - /// The message. - /// The mailbox address to use for sending the message. - /// The mailbox addresses that should receive the message. - /// The cancellation token. - /// The progress reporting mechanism. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before sending a message. - /// - /// - /// A sender has not been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// Internationalized formatting was requested but is not supported by the server. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - public override void Send (FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable recipients, CancellationToken cancellationToken = default (CancellationToken), ITransferProgress progress = null) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (sender == null) - throw new ArgumentNullException (nameof (sender)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - var unique = new HashSet (StringComparer.OrdinalIgnoreCase); - var rcpts = new List (); - - AddUnique (rcpts, unique, recipients); - - if (rcpts.Count == 0) - throw new InvalidOperationException ("No recipients have been specified."); - - SendAsync (options, message, sender, rcpts, false, cancellationToken, progress).GetAwaiter ().GetResult (); - } - - #endregion - - async Task ExpandAsync (string alias, bool doAsync, CancellationToken cancellationToken) - { - if (alias == null) - throw new ArgumentNullException (nameof (alias)); - - if (alias.Length == 0) - throw new ArgumentException ("The alias cannot be empty.", nameof (alias)); - - if (alias.IndexOfAny (new [] { '\r', '\n' }) != -1) - throw new ArgumentException ("The alias cannot contain newline characters.", nameof (alias)); - - CheckDisposed (); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The SmtpClient is not connected."); - - var response = await SendCommandAsync (string.Format ("EXPN {0}", alias), doAsync, cancellationToken).ConfigureAwait (false); - - if (response.StatusCode != SmtpStatusCode.Ok) - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - - var lines = response.Response.Split ('\n'); - var list = new InternetAddressList (); - - for (int i = 0; i < lines.Length; i++) { - InternetAddress address; - - if (InternetAddress.TryParse (lines[i], out address)) - list.Add (address); - } - - return list; - } - - /// - /// Expand a mailing address alias. - /// - /// - /// Expands a mailing address alias. - /// - /// The expanded list of mailbox addresses. - /// The mailing address alias. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before verifying the existence of an address. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - public InternetAddressList Expand (string alias, CancellationToken cancellationToken = default (CancellationToken)) - { - return ExpandAsync (alias, false, cancellationToken).GetAwaiter ().GetResult (); - } - - async Task VerifyAsync (string address, bool doAsync, CancellationToken cancellationToken) - { - if (address == null) - throw new ArgumentNullException (nameof (address)); - - if (address.Length == 0) - throw new ArgumentException ("The address cannot be empty.", nameof (address)); - - if (address.IndexOfAny (new [] { '\r', '\n' }) != -1) - throw new ArgumentException ("The address cannot contain newline characters.", nameof (address)); - - CheckDisposed (); - - if (!IsConnected) - throw new ServiceNotConnectedException ("The SmtpClient is not connected."); - - var response = await SendCommandAsync (string.Format ("VRFY {0}", address), doAsync, cancellationToken).ConfigureAwait (false); - - if (response.StatusCode == SmtpStatusCode.Ok) - return MailboxAddress.Parse (response.Response); - - throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); - } - - /// - /// Verify the existence of a mailbox address. - /// - /// - /// Verifies the existence a mailbox address with the SMTP server, returning the expanded - /// mailbox address if it exists. - /// - /// The expanded mailbox address. - /// The mailbox address. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is an empty string. - /// - /// - /// The has been disposed. - /// - /// - /// The is not connected. - /// - /// - /// Authentication is required before verifying the existence of an address. - /// - /// - /// The operation has been canceled. - /// - /// - /// An I/O error occurred. - /// - /// - /// The SMTP command failed. - /// - /// - /// An SMTP protocol exception occurred. - /// - public MailboxAddress Verify (string address, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (address, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - disposed = true; - Disconnect (null, 0, SecureSocketOptions.None, false); - } - - base.Dispose (disposed); - } - } -} diff --git a/src/MailKit/Net/Smtp/SmtpCommandException.cs b/src/MailKit/Net/Smtp/SmtpCommandException.cs deleted file mode 100644 index f2f0460..0000000 --- a/src/MailKit/Net/Smtp/SmtpCommandException.cs +++ /dev/null @@ -1,258 +0,0 @@ -// -// SmtpCommandException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -using MimeKit; - -namespace MailKit.Net.Smtp { - /// - /// An enumeration of the possible error codes that may be reported by a . - /// - /// - /// An enumeration of the possible error codes that may be reported by a . - /// - /// - /// - /// - public enum SmtpErrorCode { - /// - /// The message was not accepted for delivery. This may happen if - /// the server runs out of available disk space. - /// - MessageNotAccepted, - - /// - /// The sender's mailbox address was not accepted. Check the - /// property for the - /// mailbox used as the sender's mailbox address. - /// - SenderNotAccepted, - - /// - /// A recipient's mailbox address was not accepted. Check the - /// property for the - /// particular recipient mailbox that was not acccepted. - /// - RecipientNotAccepted, - - /// - /// An unexpected status code was returned by the server. - /// For more details, the - /// property may provide some additional hints. - /// - UnexpectedStatusCode, - } - - /// - /// An SMTP protocol exception. - /// - /// - /// The exception that is thrown when an SMTP command fails. Unlike a , - /// a does not require the to be reconnected. - /// - /// - /// - /// -#if SERIALIZABLE - [Serializable] -#endif - public class SmtpCommandException : CommandException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the serialized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected SmtpCommandException (SerializationInfo info, StreamingContext context) : base (info, context) - { - MailboxAddress mailbox; - string value; - - value = info.GetString ("Mailbox"); - if (!string.IsNullOrEmpty (value) && MailboxAddress.TryParse (value, out mailbox)) - Mailbox = mailbox; - - ErrorCode = (SmtpErrorCode) info.GetValue ("ErrorCode", typeof (SmtpErrorCode)); - StatusCode = (SmtpStatusCode) info.GetValue ("StatusCode", typeof (SmtpStatusCode)); - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error code. - /// The status code. - /// The rejected mailbox. - /// The error message. - /// The inner exception. - public SmtpCommandException (SmtpErrorCode code, SmtpStatusCode status, MailboxAddress mailbox, string message, Exception innerException) : base (message, innerException) - { - StatusCode = status; - Mailbox = mailbox; - ErrorCode = code; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error code. - /// The status code. - /// The rejected mailbox. - /// The error message. - public SmtpCommandException (SmtpErrorCode code, SmtpStatusCode status, MailboxAddress mailbox, string message) : base (message) - { - StatusCode = status; - Mailbox = mailbox; - ErrorCode = code; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error code. - /// The status code.> - /// The error message. - /// The inner exception. - public SmtpCommandException (SmtpErrorCode code, SmtpStatusCode status, string message, Exception innerException) : base (message, innerException) - { - StatusCode = status; - ErrorCode = code; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error code. - /// The status code.> - /// The error message. - public SmtpCommandException (SmtpErrorCode code, SmtpStatusCode status, string message) : base (message) - { - StatusCode = status; - ErrorCode = code; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Serializes the state of the . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - if (Mailbox != null) - info.AddValue ("Mailbox", Mailbox.ToString ()); - else - info.AddValue ("Mailbox", string.Empty); - - info.AddValue ("ErrorCode", ErrorCode, typeof (SmtpErrorCode)); - info.AddValue ("StatusCode", StatusCode, typeof (SmtpStatusCode)); - } -#endif - - /// - /// Gets the error code which may provide additional information. - /// - /// - /// The error code can be used to programatically deal with the - /// exception without necessarily needing to display the raw - /// exception message to the user. - /// - /// - /// - /// - /// The status code. - public SmtpErrorCode ErrorCode { - get; private set; - } - - /// - /// Gets the mailbox that the error occurred on. - /// - /// - /// This property will only be available when the - /// value is either or - /// and may be used - /// to help the user decide how to proceed. - /// - /// - /// - /// - /// The mailbox. - public MailboxAddress Mailbox { - get; private set; - } - - /// - /// Gets the status code returned by the SMTP server. - /// - /// - /// The raw SMTP status code that resulted in the - /// being thrown. - /// - /// - /// - /// - /// The status code. - public SmtpStatusCode StatusCode { - get; private set; - } - } -} diff --git a/src/MailKit/Net/Smtp/SmtpDataFilter.cs b/src/MailKit/Net/Smtp/SmtpDataFilter.cs deleted file mode 100644 index a5e81d6..0000000 --- a/src/MailKit/Net/Smtp/SmtpDataFilter.cs +++ /dev/null @@ -1,140 +0,0 @@ -// -// SmtpDataFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MimeKit.IO.Filters; - -namespace MailKit.Net.Smtp { - /// - /// An SMTP filter designed to format a message stream for the DATA command. - /// - /// - /// A special stream filter that escapes lines beginning with a '.' as needed when - /// sending a message via the SMTP protocol or when saving a message to an IIS - /// message pickup directory. - /// - /// - /// - /// - public class SmtpDataFilter : MimeFilterBase - { - bool bol; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// - /// - /// - public SmtpDataFilter () - { - bol = true; - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - int inputEnd = startIndex + length; - bool escape = bol; - int ndots = 0; - int crlf = 0; - - for (int i = startIndex; i < inputEnd; i++) { - byte c = input[i]; - - if (c == (byte) '.' && escape) { - escape = false; - ndots++; - } else { - escape = c == (byte) '\n'; - } - } - - if (flush && !escape) - crlf = 2; - - if (ndots + crlf == 0) { - outputIndex = startIndex; - outputLength = length; - bol = escape; - return input; - } - - EnsureOutputSize (length + ndots + crlf, false); - int index = 0; - - for (int i = startIndex; i < inputEnd; i++) { - byte c = input[i]; - - if (c == (byte) '.' && bol) { - OutputBuffer[index++] = (byte) '.'; - bol = false; - } else { - bol = c == (byte) '\n'; - } - - OutputBuffer[index++] = c; - } - - if (crlf > 0) { - OutputBuffer[index++] = (byte) '\r'; - OutputBuffer[index++] = (byte) '\n'; - } - - outputLength = index; - outputIndex = 0; - - return OutputBuffer; - } - - /// - /// Reset the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - base.Reset (); - bol = true; - } - } -} diff --git a/src/MailKit/Net/Smtp/SmtpProtocolException.cs b/src/MailKit/Net/Smtp/SmtpProtocolException.cs deleted file mode 100644 index aa973cd..0000000 --- a/src/MailKit/Net/Smtp/SmtpProtocolException.cs +++ /dev/null @@ -1,101 +0,0 @@ -// -// SmtpProtocolException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit.Net.Smtp { - /// - /// An SMTP protocol exception. - /// - /// - /// The exception that is thrown when there is an error communicating with an SMTP server. An - /// is typically fatal and requires the - /// to be reconnected. - /// - /// - /// - /// -#if SERIALIZABLE - [Serializable] -#endif - public class SmtpProtocolException : ProtocolException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the serialized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected SmtpProtocolException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The inner exception. - public SmtpProtocolException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public SmtpProtocolException (string message) : base (message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public SmtpProtocolException () - { - } - } -} diff --git a/src/MailKit/Net/Smtp/SmtpResponse.cs b/src/MailKit/Net/Smtp/SmtpResponse.cs deleted file mode 100644 index dd1c598..0000000 --- a/src/MailKit/Net/Smtp/SmtpResponse.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// SmtpResponse.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Net.Smtp { - /// - /// An SMTP command response. - /// - /// - /// An SMTP command response. - /// - public class SmtpResponse - { - /// - /// Get the status code. - /// - /// - /// Gets the status code. - /// - /// The status code. - public SmtpStatusCode StatusCode { get; private set; } - - /// - /// Get the response text. - /// - /// - /// Gets the response text. - /// - /// The response text. - public string Response { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The status code. - /// The response text. - public SmtpResponse (SmtpStatusCode code, string response) - { - StatusCode = code; - Response = response; - } - } -} diff --git a/src/MailKit/Net/Smtp/SmtpStatusCode.cs b/src/MailKit/Net/Smtp/SmtpStatusCode.cs deleted file mode 100644 index b97cc2d..0000000 --- a/src/MailKit/Net/Smtp/SmtpStatusCode.cs +++ /dev/null @@ -1,190 +0,0 @@ -// -// SmtpStatusCode.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Net.Smtp { - /// - /// An enumeration of possible SMTP status codes. - /// - /// - /// An enumeration of possible SMTP status codes. - /// - public enum SmtpStatusCode { - /// - /// The "system status" status code. - /// - SystemStatus = 211, - - /// - /// The "help" status code. - /// - HelpMessage = 214, - - /// - /// The "service ready" status code. - /// - ServiceReady = 220, - - /// - /// The "service closing transmission channel" status code. - /// - ServiceClosingTransmissionChannel = 221, - - /// - /// The "authentication successful" status code. - /// - AuthenticationSuccessful = 235, - - /// - /// The general purpose "OK" status code. - /// - Ok = 250, - - /// - /// The "User not local; will forward" status code. - /// - UserNotLocalWillForward = 251, - - /// - /// The "cannot verify user; will attempt delivery" status code. - /// - CannotVerifyUserWillAttemptDelivery = 252, - - /// - /// The "authentication challenge" status code. - /// - AuthenticationChallenge = 334, - - /// - /// The "start mail input" status code. - /// - StartMailInput = 354, - - /// - /// The "service not available" status code. - /// - ServiceNotAvailable = 421, - - /// - /// The "password transition needed" status code. - /// - PasswordTransitionNeeded = 432, - - /// - /// The "mailbox busy" status code. - /// - MailboxBusy = 450, - - /// - /// The "error in processing" status code. - /// - ErrorInProcessing = 451, - - /// - /// The "insufficient storage" status code. - /// - InsufficientStorage = 452, - - /// - /// The "temporary authentication failure" status code. - /// - TemporaryAuthenticationFailure = 454, - - /// - /// The "command unrecognized" status code. - /// - CommandUnrecognized = 500, - - /// - /// The "syntax error" status code. - /// - SyntaxError = 501, - - /// - /// The "command not implemented" status code. - /// - CommandNotImplemented = 502, - - /// - /// The "bad command sequence" status code. - /// - BadCommandSequence = 503, - - /// - /// The "command parameter not implemented" status code. - /// - CommandParameterNotImplemented = 504, - - /// - /// The "authentication required" status code. - /// - AuthenticationRequired = 530, - - /// - /// The "authentication mechanism too weak" status code. - /// - AuthenticationMechanismTooWeak = 534, - - /// - /// The "authentication invalid credentials" status code. - /// - AuthenticationInvalidCredentials = 535, - - /// - /// The "encryption required for authentication mechanism" status code. - /// - EncryptionRequiredForAuthenticationMechanism = 538, - - /// - /// The "mailbox unavailable" status code. - /// - MailboxUnavailable = 550, - - /// - /// The "user not local try alternate path" status code. - /// - UserNotLocalTryAlternatePath = 551, - - /// - /// The "exceeded storage allocation" status code. - /// - ExceededStorageAllocation = 552, - - /// - /// The "mailbox name not allowed" status code. - /// - MailboxNameNotAllowed = 553, - - /// - /// The "transaction failed" status code. - /// - TransactionFailed = 554, - - /// - /// The "mail from/rcpt to parameters not recognized or not implemented" status code. - /// - MailFromOrRcptToParametersNotRecognizedOrNotImplemented = 555, - } -} diff --git a/src/MailKit/Net/Smtp/SmtpStream.cs b/src/MailKit/Net/Smtp/SmtpStream.cs deleted file mode 100644 index e175373..0000000 --- a/src/MailKit/Net/Smtp/SmtpStream.cs +++ /dev/null @@ -1,903 +0,0 @@ -// -// SmtpStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Net.Sockets; -using System.Net.Security; -using System.Threading.Tasks; - -using MimeKit.IO; - -using Buffer = System.Buffer; -using NetworkStream = MailKit.Net.NetworkStream; - -namespace MailKit.Net.Smtp { - /// - /// A stream for communicating with an SMTP server. - /// - /// - /// A stream capable of reading SMTP server responses. - /// - class SmtpStream : Stream, ICancellableStream - { - static readonly Encoding Latin1; - static readonly Encoding UTF8; - const int BlockSize = 4096; - - // I/O buffering - readonly byte[] input = new byte[BlockSize]; - readonly byte[] output = new byte[BlockSize]; - int outputIndex; - - readonly IProtocolLogger logger; - int inputIndex, inputEnd; - bool disposed; - - static SmtpStream () - { - UTF8 = Encoding.GetEncoding (65001, new EncoderExceptionFallback (), new DecoderExceptionFallback ()); - - try { - Latin1 = Encoding.GetEncoding (28591); - } catch (NotSupportedException) { - Latin1 = Encoding.GetEncoding (1252); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The underlying network stream. - /// The protocol logger. - public SmtpStream (Stream source, IProtocolLogger protocolLogger) - { - logger = protocolLogger; - IsConnected = true; - Stream = source; - } - - /// - /// Get or sets the underlying network stream. - /// - /// - /// Gets or sets the underlying network stream. - /// - /// The underlying network stream. - public Stream Stream { - get; internal set; - } - - /// - /// Get whether or not the stream is connected. - /// - /// - /// Gets whether or not the stream is connected. - /// - /// true if the stream is connected; otherwise, false. - public bool IsConnected { - get; private set; - } - - /// - /// Get whether the stream supports reading. - /// - /// - /// Gets whether the stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return Stream.CanRead; } - } - - /// - /// Get whether the stream supports writing. - /// - /// - /// Gets whether the stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return Stream.CanWrite; } - } - - /// - /// Get whether the stream supports seeking. - /// - /// - /// Gets whether the stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return false; } - } - - /// - /// Get whether the stream supports I/O timeouts. - /// - /// - /// Gets whether the stream supports I/O timeouts. - /// - /// true if the stream supports I/O timeouts; otherwise, false. - public override bool CanTimeout { - get { return Stream.CanTimeout; } - } - - /// - /// Get or set a value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// - /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// A value, in milliseconds, that determines how long the stream will attempt to read before timing out. - /// The read timeout. - public override int ReadTimeout { - get { return Stream.ReadTimeout; } - set { Stream.ReadTimeout = value; } - } - - /// - /// Get or set a value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// - /// Gets or sets a value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// A value, in milliseconds, that determines how long the stream will attempt to write before timing out. - /// The write timeout. - public override int WriteTimeout { - get { return Stream.WriteTimeout; } - set { Stream.WriteTimeout = value; } - } - - /// - /// Get or set the position within the current stream. - /// - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return Stream.Position; } - set { throw new NotSupportedException (); } - } - - /// - /// Get the length of the stream, in bytes. - /// - /// - /// Gets the length of the stream, in bytes. - /// - /// A long value representing the length of the stream in bytes. - /// The length of the stream. - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Length { - get { return Stream.Length; } - } - - async Task ReadAheadAsync (bool doAsync, CancellationToken cancellationToken) - { - int left = inputEnd - inputIndex; - int index, nread; - - if (left > 0) { - if (inputIndex > 0) { - // move all of the remaining input to the beginning of the buffer - Buffer.BlockCopy (input, inputIndex, input, 0, left); - inputEnd = left; - inputIndex = 0; - } - } else { - inputIndex = 0; - inputEnd = 0; - } - - left = input.Length - inputEnd; - index = inputEnd; - - try { - var network = Stream as NetworkStream; - - cancellationToken.ThrowIfCancellationRequested (); - - if (doAsync) { - nread = await Stream.ReadAsync (input, index, left, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectRead, cancellationToken); - nread = Stream.Read (input, index, left); - } - - if (nread > 0) { - logger.LogServer (input, index, nread); - inputEnd += nread; - } else { - throw new SmtpProtocolException ("The SMTP server has unexpectedly disconnected."); - } - } catch { - IsConnected = false; - throw; - } - - return inputEnd - inputIndex; - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (SmtpStream)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public int Read (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { -#if false // Note: this code will never get called as we always use ReadResponse() instead. - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - int length = inputEnd - inputIndex; - int n; - - if (length < count && length <= ReadAheadSize) - await ReadAheadAsync (cancellationToken).ConfigureAwait (false); - - length = inputEnd - inputIndex; - n = Math.Min (count, length); - - Buffer.BlockCopy (input, inputIndex, buffer, offset, n); - inputIndex += n; - - return n; -#else - throw new NotImplementedException (); -#endif - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - return Read (buffer, offset, count, CancellationToken.None); - } - - /// - /// Asynchronously reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer. - /// The buffer offset. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { -#if false // Note: this code will never get called as we always use ReadResponse() instead. - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - int length = inputEnd - inputIndex; - int n; - - if (length < count && length <= ReadAheadSize) - await ReadAheadAsync (cancellationToken).ConfigureAwait (false); - - length = inputEnd - inputIndex; - n = Math.Min (count, length); - - Buffer.BlockCopy (input, inputIndex, buffer, offset, n); - inputIndex += n; - - return n; -#else - throw new NotImplementedException (); -#endif - } - - static bool TryParseInt32 (byte[] text, ref int index, int endIndex, out int value) - { - int startIndex = index; - - value = 0; - - while (index < endIndex && text[index] >= (byte) '0' && text[index] <= (byte) '9') - value = (value * 10) + (text[index++] - (byte) '0'); - - return index > startIndex; - } - - async Task ReadResponseAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - using (var memory = new MemoryStream ()) { - bool needInput = inputIndex == inputEnd; - bool complete = false; - bool newLine = true; - bool more = true; - int code = 0; - - do { - if (needInput) { - await ReadAheadAsync (doAsync, cancellationToken).ConfigureAwait (false); - needInput = false; - } - - complete = false; - - do { - int startIndex = inputIndex; - - if (newLine && inputIndex < inputEnd) { - int value; - - if (!TryParseInt32 (input, ref inputIndex, inputEnd, out value)) - throw new SmtpProtocolException ("Unable to parse status code returned by the server."); - - if (inputIndex == inputEnd) { - inputIndex = startIndex; - needInput = true; - break; - } - - if (code == 0) { - code = value; - } else if (value != code) { - throw new SmtpProtocolException ("The status codes returned by the server did not match."); - } - - newLine = false; - - if (input[inputIndex] != (byte) '\r' && input[inputIndex] != (byte) '\n') - more = input[inputIndex++] == (byte) '-'; - else - more = false; - - startIndex = inputIndex; - } - - while (inputIndex < inputEnd && input[inputIndex] != (byte) '\r' && input[inputIndex] != (byte) '\n') - inputIndex++; - - memory.Write (input, startIndex, inputIndex - startIndex); - - if (inputIndex < inputEnd && input[inputIndex] == (byte) '\r') - inputIndex++; - - if (inputIndex < inputEnd && input[inputIndex] == (byte) '\n') { - if (more) - memory.WriteByte (input[inputIndex]); - complete = true; - newLine = true; - inputIndex++; - } - } while (more && inputIndex < inputEnd); - - if (inputIndex == inputEnd) - needInput = true; - } while (more || !complete); - - string message = null; - - try { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - message = UTF8.GetString (memory.GetBuffer (), 0, (int) memory.Length); -#else - message = UTF8.GetString (memory.ToArray (), 0, (int) memory.Length); -#endif - } catch (DecoderFallbackException) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - message = Latin1.GetString (memory.GetBuffer (), 0, (int) memory.Length); -#else - message = Latin1.GetString (memory.ToArray (), 0, (int) memory.Length); -#endif - } - - return new SmtpResponse ((SmtpStatusCode) code, message); - } - } - - /// - /// Read an SMTP server response. - /// - /// - /// Reads a full command response from the SMTP server. - /// - /// The response. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An SMTP protocol error occurred. - /// - public SmtpResponse ReadResponse (CancellationToken cancellationToken) - { - return ReadResponseAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously read an SMTP server response. - /// - /// - /// Reads a full command response from the SMTP server. - /// - /// The response. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - /// - /// An SMTP protocol error occurred. - /// - public Task ReadResponseAsync (CancellationToken cancellationToken) - { - return ReadResponseAsync (true, cancellationToken); - } - - async Task WriteAsync (byte[] buffer, int offset, int count, bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - try { - var network = NetworkStream.Get (Stream); - int index = offset; - int left = count; - - while (left > 0) { - int n = Math.Min (BlockSize - outputIndex, left); - - if (outputIndex > 0 || n < BlockSize) { - // append the data to the output buffer - Buffer.BlockCopy (buffer, index, output, outputIndex, n); - outputIndex += n; - index += n; - left -= n; - } - - if (outputIndex == BlockSize) { - // flush the output buffer - if (doAsync) { - await Stream.WriteAsync (output, 0, BlockSize, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (output, 0, BlockSize); - } - logger.LogClient (output, 0, BlockSize); - outputIndex = 0; - } - - if (outputIndex == 0) { - // write blocks of data to the stream without buffering - while (left >= BlockSize) { - if (doAsync) { - await Stream.WriteAsync (buffer, index, BlockSize, cancellationToken).ConfigureAwait (false); - } else { - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (buffer, index, BlockSize); - } - logger.LogClient (buffer, index, BlockSize); - index += BlockSize; - left -= BlockSize; - } - } - } - } catch (Exception ex) { - IsConnected = false; - if (!(ex is OperationCanceledException)) - cancellationToken.ThrowIfCancellationRequested (); - throw; - } - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void Write (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - WriteAsync (buffer, offset, count, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - Write (buffer, offset, count, CancellationToken.None); - } - - /// - /// Asynchronously writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return WriteAsync (buffer, offset, count, true, cancellationToken); - } - - async Task FlushAsync (bool doAsync, CancellationToken cancellationToken) - { - CheckDisposed (); - - if (outputIndex == 0) - return; - - try { - if (doAsync) { - await Stream.WriteAsync (output, 0, outputIndex, cancellationToken).ConfigureAwait (false); - await Stream.FlushAsync (cancellationToken).ConfigureAwait (false); - } else { - var network = NetworkStream.Get (Stream); - - network?.Poll (SelectMode.SelectWrite, cancellationToken); - Stream.Write (output, 0, outputIndex); - Stream.Flush (); - } - logger.LogClient (output, 0, outputIndex); - outputIndex = 0; - } catch (Exception ex) { - IsConnected = false; - if (!(ex is OperationCanceledException)) - cancellationToken.ThrowIfCancellationRequested (); - throw; - } - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void Flush (CancellationToken cancellationToken) - { - FlushAsync (false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Flush () - { - Flush (CancellationToken.None); - } - - /// - /// Asynchronously clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task FlushAsync (CancellationToken cancellationToken) - { - return FlushAsync (true, cancellationToken); - } - - /// - /// Sets the position within the current stream. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// The stream does not support seeking. - /// - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - /// - /// Sets the length of the stream. - /// - /// The desired length of the stream in bytes. - /// - /// The stream does not support setting the length. - /// - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - IsConnected = false; - Stream.Dispose (); - } - - disposed = true; - - base.Dispose (disposing); - } - } -} diff --git a/src/MailKit/Net/SocketUtils.cs b/src/MailKit/Net/SocketUtils.cs deleted file mode 100644 index 62e2ea9..0000000 --- a/src/MailKit/Net/SocketUtils.cs +++ /dev/null @@ -1,118 +0,0 @@ -// -// SocketUtils.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Threading; -using System.Net.Sockets; -using System.Threading.Tasks; - -namespace MailKit.Net -{ - static class SocketUtils - { - public static async Task ConnectAsync (string host, int port, IPEndPoint localEndPoint, bool doAsync, CancellationToken cancellationToken) - { - IPAddress[] ipAddresses; - - cancellationToken.ThrowIfCancellationRequested (); - - if (doAsync) { - ipAddresses = await Dns.GetHostAddressesAsync (host).ConfigureAwait (false); - } else { - ipAddresses = Dns.GetHostAddressesAsync (host).GetAwaiter ().GetResult (); - } - - for (int i = 0; i < ipAddresses.Length; i++) { - cancellationToken.ThrowIfCancellationRequested (); - - var socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); - - try { - if (localEndPoint != null) - socket.Bind (localEndPoint); - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (doAsync || cancellationToken.CanBeCanceled) { - var tcs = new TaskCompletionSource (); - - using (var registration = cancellationToken.Register (() => tcs.TrySetCanceled (), false)) { - var ar = socket.BeginConnect (ipAddresses[i], port, e => tcs.TrySetResult (true), null); - - if (doAsync) - await tcs.Task.ConfigureAwait (false); - else - tcs.Task.GetAwaiter ().GetResult (); - - socket.EndConnect (ar); - } - } else { - socket.Connect (ipAddresses[i], port); - } -#else - socket.Connect (ipAddresses[i], port); -#endif - - return socket; - } catch (OperationCanceledException) { - socket.Dispose (); - throw; - } catch { - socket.Dispose (); - - if (i + 1 == ipAddresses.Length) - throw; - } - } - - throw new IOException (string.Format ("Failed to resolve host: {0}", host)); - } - - public static async Task ConnectAsync (string host, int port, IPEndPoint localEndPoint, int timeout, bool doAsync, CancellationToken cancellationToken) - { - using (var ts = new CancellationTokenSource (timeout)) { - using (var linked = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken, ts.Token)) { - try { - return await ConnectAsync (host, port, localEndPoint, doAsync, linked.Token).ConfigureAwait (false); - } catch (OperationCanceledException) { - if (!cancellationToken.IsCancellationRequested) - throw new TimeoutException (); - throw; - } - } - } - } - - public static void Poll (Socket socket, SelectMode mode, CancellationToken cancellationToken) - { - do { - cancellationToken.ThrowIfCancellationRequested (); - // wait 1/4 second and then re-check for cancellation - } while (!socket.Poll (250000, mode)); - } - } -} diff --git a/src/MailKit/Net/SslStream.cs b/src/MailKit/Net/SslStream.cs deleted file mode 100644 index cd2fb03..0000000 --- a/src/MailKit/Net/SslStream.cs +++ /dev/null @@ -1,44 +0,0 @@ -// -// SslStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.IO; -using System.Threading; -using System.Net.Security; -using System.Threading.Tasks; - -namespace MailKit.Net -{ - class SslStream : System.Net.Security.SslStream - { - public SslStream (Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback) : base (innerStream, leaveInnerStreamOpen, userCertificateValidationCallback) - { - } - - new public Stream InnerStream { - get { return base.InnerStream; } - } - } -} diff --git a/src/MailKit/NullProtocolLogger.cs b/src/MailKit/NullProtocolLogger.cs deleted file mode 100644 index fe0fa47..0000000 --- a/src/MailKit/NullProtocolLogger.cs +++ /dev/null @@ -1,113 +0,0 @@ -// -// NullProtocolLogger.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// A protocol logger that does not log to anywhere. - /// - /// - /// By default, the , - /// , and - /// all use a - /// . - /// - public sealed class NullProtocolLogger : IProtocolLogger - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public NullProtocolLogger () - { - } - - #region IProtocolLogger implementation - - /// - /// Logs a connection to the specified URI. - /// - /// - /// This method does nothing. - /// - /// The URI. - public void LogConnect (Uri uri) - { - } - - /// - /// Logs a sequence of bytes sent by the client. - /// - /// - /// This method does nothing. - /// - /// The buffer to log. - /// The offset of the first byte to log. - /// The number of bytes to log. - /// - /// is null. - /// - public void LogClient (byte[] buffer, int offset, int count) - { - } - - /// - /// Logs a sequence of bytes sent by the server. - /// - /// - /// This method does nothing. - /// - /// The buffer to log. - /// The offset of the first byte to log. - /// The number of bytes to log. - /// - /// is null. - /// - public void LogServer (byte[] buffer, int offset, int count) - { - } - - #endregion - - #region IDisposable implementation - - /// - /// Releases all resource used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. - public void Dispose () - { - } - - #endregion - } -} diff --git a/src/MailKit/ProgressStream.cs b/src/MailKit/ProgressStream.cs deleted file mode 100644 index 9748a56..0000000 --- a/src/MailKit/ProgressStream.cs +++ /dev/null @@ -1,185 +0,0 @@ -// -// ProgressStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -using MimeKit.IO; - -namespace MailKit { - class ProgressStream : Stream, ICancellableStream - { - readonly ICancellableStream cancellable; - - public ProgressStream (Stream source, Action update) - { - if (source == null) - throw new ArgumentNullException (nameof (source)); - - if (update == null) - throw new ArgumentNullException (nameof (update)); - - cancellable = source as ICancellableStream; - Source = source; - Update = update; - } - - public Stream Source { - get; private set; - } - - Action Update { - get; set; - } - - public override bool CanRead { - get { return Source.CanRead; } - } - - public override bool CanWrite { - get { return Source.CanWrite; } - } - - public override bool CanSeek { - get { return false; } - } - - public override bool CanTimeout { - get { return Source.CanTimeout; } - } - - public override long Length { - get { return Source.Length; } - } - - public override long Position { - get { return Source.Position; } - set { Seek (value, SeekOrigin.Begin); } - } - - public override int ReadTimeout { - get { return Source.ReadTimeout; } - set { Source.ReadTimeout = value; } - } - - public override int WriteTimeout { - get { return Source.WriteTimeout; } - set { Source.WriteTimeout = value; } - } - - public int Read (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - int n; - - if (cancellable != null) { - if ((n = cancellable.Read (buffer, offset, count, cancellationToken)) > 0) - Update (n); - } else { - if ((n = Source.Read (buffer, offset, count)) > 0) - Update (n); - } - - return n; - } - - public override int Read (byte[] buffer, int offset, int count) - { - int n; - - if ((n = Source.Read (buffer, offset, count)) > 0) - Update (n); - - return n; - } - - public override async Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - int n; - - if ((n = await Source.ReadAsync (buffer, offset, count, cancellationToken).ConfigureAwait (false)) > 0) - Update (n); - - return n; - } - - public void Write (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (cancellable != null) - cancellable.Write (buffer, offset, count, cancellationToken); - else - Source.Write (buffer, offset, count); - - if (count > 0) - Update (count); - } - - public override void Write (byte[] buffer, int offset, int count) - { - Source.Write (buffer, offset, count); - - if (count > 0) - Update (count); - } - - public override async Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - await Source.WriteAsync (buffer, offset, count, cancellationToken).ConfigureAwait (false); - - if (count > 0) - Update (count); - } - - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException ("The stream does not support seeking."); - } - - public void Flush (CancellationToken cancellationToken) - { - if (cancellable != null) - cancellable.Flush (cancellationToken); - else - Source.Flush (); - } - - public override void Flush () - { - Source.Flush (); - } - - public override Task FlushAsync (CancellationToken cancellationToken) - { - return Source.FlushAsync (cancellationToken); - } - - public override void SetLength (long value) - { - throw new NotSupportedException ("The stream does not support resizing."); - } - } -} diff --git a/src/MailKit/Properties/AssemblyInfo.cs b/src/MailKit/Properties/AssemblyInfo.cs deleted file mode 100644 index b4f4c12..0000000 --- a/src/MailKit/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,84 +0,0 @@ -// -// AssemblyInfo.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle ("MailKit")] -[assembly: AssemblyDescription ("A cross-platform mail client library.")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany (".NET Foundation")] -[assembly: AssemblyProduct ("MailKit")] -[assembly: AssemblyCopyright ("Copyright © 2013-2020 .NET Foundation and Contributors")] -[assembly: AssemblyTrademark (".NET Foundation")] -[assembly: AssemblyCulture ("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible (true)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid ("2fe79b66-d107-45da-9493-175f59c4a53c")] -[assembly: InternalsVisibleTo ("UnitTests, PublicKey=002400000480000094000000060200" + - "0000240000525341310004000011000000cde209732ce60a8fa70ee643cb32e9bf8149b61018c5" + - "b166489b8a5cae44f1f88ca97ab9d9e035421933a6f0d556acc7c2219ae1464e35386ca1e239aa" + - "42508b9edbb4164bfa82aa2a0f4cd983d9e5ba2acfe08a10a2093e2b2bf8408eef43114db89b39" + - "99c59af1d3dc2c9f0cdbf51074e9a482cf09c9116ae1c5543ce8ff9b")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Micro Version -// Build Number -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -// -// Note: AssemblyVersion is what the CLR matches against at runtime, so be careful -// about updating it. The AssemblyFileVersion is the official release version while -// the AssemblyInformationalVersion is just used as a display version. -// -// Based on my current understanding, AssemblyVersion is essentially the "API Version" -// and so should only be updated when the API changes. The other 2 Version attributes -// represent the "Release Version". -// -// Making releases: -// -// If any classes, methods, or enum values have been added, bump the Micro Version -// in all version attributes and set the Build Number back to 0. -// -// If there have only been bug fixes, bump the Micro Version and/or the Build Number -// in the AssemblyFileVersion attribute. -[assembly: AssemblyInformationalVersion ("2.7.0.0")] -[assembly: AssemblyFileVersion ("2.7.0.0")] -[assembly: AssemblyVersion ("2.7.0.0")] diff --git a/src/MailKit/ProtocolException.cs b/src/MailKit/ProtocolException.cs deleted file mode 100644 index 88b08c5..0000000 --- a/src/MailKit/ProtocolException.cs +++ /dev/null @@ -1,100 +0,0 @@ -// -// ProtocolException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit { - /// - /// The exception that is thrown when there is a protocol error. - /// - /// - /// A can be thrown by any of the various client - /// methods in MailKit. - /// Since many protocol exceptions are fatal, it is important to check whether - /// or not the client is still connected using the - /// property when this exception is thrown. - /// -#if SERIALIZABLE - [Serializable] -#endif - public abstract class ProtocolException : Exception - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The serialization info. - /// The streaming context. - [SecuritySafeCritical] - protected ProtocolException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// An inner exception. - protected ProtocolException (string message, Exception innerException) : base (message, innerException) - { - HelpLink = "https://github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLog"; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - protected ProtocolException (string message) : base (message) - { - HelpLink = "https://github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLog"; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - protected ProtocolException () - { - HelpLink = "https://github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLog"; - } - } -} diff --git a/src/MailKit/ProtocolLogger.cs b/src/MailKit/ProtocolLogger.cs deleted file mode 100644 index f0682cf..0000000 --- a/src/MailKit/ProtocolLogger.cs +++ /dev/null @@ -1,341 +0,0 @@ -// -// ProtocolLogger.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; - -namespace MailKit { - /// - /// A default protocol logger for logging the communication between a client and server. - /// - /// - /// A default protocol logger for logging the communication between a client and server. - /// - /// - /// - /// - public class ProtocolLogger : IProtocolLogger - { - static byte[] defaultClientPrefix = Encoding.ASCII.GetBytes ("C: "); - static byte[] defaultServerPrefix = Encoding.ASCII.GetBytes ("S: "); - - byte[] clientPrefix = defaultClientPrefix; - byte[] serverPrefix = defaultServerPrefix; - readonly Stream stream; - readonly bool leaveOpen; - bool clientMidline; - bool serverMidline; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new to log to a specified file. The file is created if it does not exist. - /// - /// - /// - /// - /// The file name. - /// true if the file should be appended to; otherwise, false. Defaults to true. - public ProtocolLogger (string fileName, bool append = true) - { - stream = File.Open (fileName, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new to log to a specified stream. - /// - /// The stream. - /// true if the stream should be left open after the protocol logger is disposed. - public ProtocolLogger (Stream stream, bool leaveOpen = false) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - this.leaveOpen = leaveOpen; - this.stream = stream; - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~ProtocolLogger () - { - Dispose (false); - } - - /// - /// Get the log stream. - /// - /// - /// Gets the log stream. - /// - /// The log sstream. - public Stream Stream { - get { return stream; } - } - - /// - /// Get or set the default client prefix to use when creating new instances. - /// - /// - /// Get or set the default client prefix to use when creating new instances. - /// - /// The default client prefix. - public static string DefaultClientPrefix - { - get { return Encoding.UTF8.GetString (defaultClientPrefix); } - set { defaultClientPrefix = Encoding.UTF8.GetBytes (value); } - } - - /// - /// Get or set the default server prefix to use when creating new instances. - /// - /// - /// Get or set the default server prefix to use when creating new instances. - /// - /// The default server prefix. - public static string DefaultServerPrefix - { - get { return Encoding.UTF8.GetString (defaultServerPrefix); } - set { defaultServerPrefix = Encoding.UTF8.GetBytes (value); } - } - - /// - /// Get or set the client prefix to use when logging client messages. - /// - /// - /// Gets or sets the client prefix to use when logging client messages. - /// - /// The client prefix. - public string ClientPrefix - { - get { return Encoding.UTF8.GetString (clientPrefix); } - set { clientPrefix = Encoding.UTF8.GetBytes (value); } - } - - /// - /// Get or set the server prefix to use when logging server messages. - /// - /// - /// Gets or sets the server prefix to use when logging server messages. - /// - /// The server prefix. - public string ServerPrefix - { - get { return Encoding.UTF8.GetString (serverPrefix); } - set { serverPrefix = Encoding.UTF8.GetBytes (value); } - } - - #region IProtocolLogger implementation - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - void Log (byte[] prefix, ref bool midline, byte[] buffer, int offset, int count) - { - int endIndex = offset + count; - int index = offset; - int start; - - while (index < endIndex) { - start = index; - - while (index < endIndex && buffer[index] != (byte) '\n') - index++; - - if (!midline) - stream.Write (prefix, 0, prefix.Length); - - if (index < endIndex && buffer[index] == (byte) '\n') { - midline = false; - index++; - } else { - midline = true; - } - - stream.Write (buffer, start, index - start); - } - - stream.Flush (); - } - - /// - /// Logs a connection to the specified URI. - /// - /// - /// Logs a connection to the specified URI. - /// - /// The URI. - /// - /// is null. - /// - /// - /// The logger has been disposed. - /// - /// - /// An I/O error occurred. - /// - public void LogConnect (Uri uri) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - var message = string.Format ("Connected to {0}\r\n", uri); - var buf = Encoding.ASCII.GetBytes (message); - - if (clientMidline || serverMidline) { - stream.WriteByte ((byte) '\r'); - stream.WriteByte ((byte) '\n'); - clientMidline = false; - serverMidline = false; - } - - stream.Write (buf, 0, buf.Length); - stream.Flush (); - } - - /// - /// Logs a sequence of bytes sent by the client. - /// - /// - /// Logs a sequence of bytes sent by the client. - /// is called by the upon every successful - /// write operation to its underlying network stream, passing the exact same , - /// , and arguments to the logging function. - /// - /// The buffer to log. - /// The offset of the first byte to log. - /// The number of bytes to log. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The logger has been disposed. - /// - /// - /// An I/O error occurred. - /// - public void LogClient (byte[] buffer, int offset, int count) - { - ValidateArguments (buffer, offset, count); - - Log (clientPrefix, ref clientMidline, buffer, offset, count); - } - - /// - /// Logs a sequence of bytes sent by the server. - /// - /// - /// Logs a sequence of bytes sent by the server. - /// is called by the upon every successful - /// read of its underlying network stream with the exact buffer that was read. - /// - /// The buffer to log. - /// The offset of the first byte to log. - /// The number of bytes to log. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes strting - /// at the specified . - /// - /// - /// The logger has been disposed. - /// - /// - /// An I/O error occurred. - /// - public void LogServer (byte[] buffer, int offset, int count) - { - ValidateArguments (buffer, offset, count); - - Log (serverPrefix, ref serverMidline, buffer, offset, count); - } - - #endregion - - #region IDisposable implementation - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected virtual void Dispose (bool disposing) - { - if (disposing && !leaveOpen) - stream.Dispose (); - } - - /// - /// Releases all resource used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After calling - /// , you must release all references to the so the garbage - /// collector can reclaim the memory that the was occupying. - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - } - - #endregion - } -} diff --git a/src/MailKit/Search/AnnotationSearchQuery.cs b/src/MailKit/Search/AnnotationSearchQuery.cs deleted file mode 100644 index 07805b4..0000000 --- a/src/MailKit/Search/AnnotationSearchQuery.cs +++ /dev/null @@ -1,105 +0,0 @@ -// -// AnnotationSearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search -{ - /// - /// An annotation-based search query. - /// - /// - /// An annotation-based search query. - /// - public class AnnotationSearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new annotation-based search query. - /// - /// The annotation entry. - /// The annotation attribute. - /// The annotation attribute value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid attribute for searching. - /// - public AnnotationSearchQuery (AnnotationEntry entry, AnnotationAttribute attribute, string value) : base (SearchTerm.Annotation) - { - if (entry == null) - throw new ArgumentNullException (nameof (entry)); - - if (attribute == null) - throw new ArgumentNullException (nameof (attribute)); - - if (attribute.Name != "value") - throw new ArgumentException ("Only the \"value\", \"value.priv\", and \"value.shared\" attributes can be searched.", nameof (attribute)); - - Attribute = attribute; - Entry = entry; - Value = value; - } - - /// - /// Get the annotation entry. - /// - /// - /// Gets the annotation entry. - /// - /// The annotation entry. - public AnnotationEntry Entry { - get; private set; - } - - /// - /// Get the annotation attribute. - /// - /// - /// Gets the annotation attribute. - /// - /// The annotation attribute. - public AnnotationAttribute Attribute { - get; private set; - } - - /// - /// Get the annotation attribute value. - /// - /// - /// Gets the annotation attribute value. - /// - /// The annotation attribute value. - public string Value { - get; private set; - } - } -} diff --git a/src/MailKit/Search/BinarySearchQuery.cs b/src/MailKit/Search/BinarySearchQuery.cs deleted file mode 100644 index 9bbd071..0000000 --- a/src/MailKit/Search/BinarySearchQuery.cs +++ /dev/null @@ -1,100 +0,0 @@ -// -// BinarySearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search { - /// - /// A binary search query such as an AND or OR expression. - /// - /// - /// A binary search query such as an AND or OR expression. - /// - public class BinarySearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new binary search query. - /// - /// THe search term. - /// The left expression. - /// The right expression. - /// - /// is null. - /// -or- - /// is null. - /// - public BinarySearchQuery (SearchTerm term, SearchQuery left, SearchQuery right) : base (term) - { - if (left == null) - throw new ArgumentNullException (nameof (left)); - - if (right == null) - throw new ArgumentNullException (nameof (right)); - - Right = right; - Left = left; - } - - /// - /// Gets the left operand of the expression. - /// - /// - /// Gets the left operand of the expression. - /// - /// The left operand. - public SearchQuery Left { - get; private set; - } - - /// - /// Gets the right operand of the expression. - /// - /// - /// Gets the right operand of the expression. - /// - /// The right operand. - public SearchQuery Right { - get; private set; - } - - internal override SearchQuery Optimize (ISearchQueryOptimizer optimizer) - { - var right = Right.Optimize (optimizer); - var left = Left.Optimize (optimizer); - SearchQuery binary; - - if (left != Left || right != Right) - binary = new BinarySearchQuery (Term, left, right); - else - binary = this; - - return optimizer.Reduce (binary); - } - } -} diff --git a/src/MailKit/Search/DateSearchQuery.cs b/src/MailKit/Search/DateSearchQuery.cs deleted file mode 100644 index c8227df..0000000 --- a/src/MailKit/Search/DateSearchQuery.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// DateSearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search { - /// - /// A date-based search query. - /// - /// - /// A date-based search query. - /// - public class DateSearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new date-based search query. - /// - /// The search term. - /// The date. - public DateSearchQuery (SearchTerm term, DateTime date) : base (term) - { - Date = date; - } - - /// - /// Gets the date value of the search query. - /// - /// - /// Gets the date value of the search query. - /// - /// The date. - public DateTime Date { - get; private set; - } - } -} diff --git a/src/MailKit/Search/FilterSearchQuery.cs b/src/MailKit/Search/FilterSearchQuery.cs deleted file mode 100644 index 80e3062..0000000 --- a/src/MailKit/Search/FilterSearchQuery.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// FilterSearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search -{ - /// - /// A filter-based search query. - /// - /// - /// A filter-based search query. - /// - public class FilterSearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// A search query that references a predefined filter. - /// - /// The name of the filter. - /// - /// is null. - /// - /// - /// is empty. - /// - public FilterSearchQuery (string name) : base (SearchTerm.Filter) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (name.Length == 0) - throw new ArgumentException ("The filter name cannot be empty.", nameof (name)); - - Name = name; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// A search query that references a predefined filter. - /// - /// The metadata tag representing the filter. - /// - /// does not reference a valid filter. - /// - public FilterSearchQuery (MetadataTag filter) : base (SearchTerm.Filter) - { - if (filter.Id.StartsWith ("/private/filters/values/", StringComparison.Ordinal)) - Name = filter.Id.Substring ("/private/filters/values/".Length); - else if (filter.Id.StartsWith ("/shared/filters/values/", StringComparison.Ordinal)) - Name = filter.Id.Substring ("/shared/filters/values/".Length); - else - throw new ArgumentException ("Metadata tag does not reference a valid filter.", nameof (filter)); - } - - /// - /// Get the name of the filter. - /// - /// - /// Gets the name of the filter. - /// - /// The name of the filter. - public string Name { - get; private set; - } - } -} diff --git a/src/MailKit/Search/HeaderSearchQuery.cs b/src/MailKit/Search/HeaderSearchQuery.cs deleted file mode 100644 index c653a81..0000000 --- a/src/MailKit/Search/HeaderSearchQuery.cs +++ /dev/null @@ -1,91 +0,0 @@ -// -// HeaderSearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search { - /// - /// A header-based search query. - /// - /// - /// A header-based search query. - /// - public class HeaderSearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new header search query. - /// - /// The header field name. - /// The value to match against. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - public HeaderSearchQuery (string field, string value) : base (SearchTerm.HeaderContains) - { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - if (field.Length == 0) - throw new ArgumentException ("Cannot search an empty header field name.", nameof (field)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Field = field; - Value = value; - } - - /// - /// Gets the header field name. - /// - /// - /// Gets the header field name. - /// - /// The header field. - public string Field { - get; private set; - } - - /// - /// Gets the value to match against. - /// - /// - /// Gets the value to match against. - /// - /// The value. - public string Value { - get; private set; - } - } -} diff --git a/src/MailKit/Search/ISearchQueryOptimizer.cs b/src/MailKit/Search/ISearchQueryOptimizer.cs deleted file mode 100644 index b55f346..0000000 --- a/src/MailKit/Search/ISearchQueryOptimizer.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -// ISearchQueryOptimizer.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Search { - interface ISearchQueryOptimizer - { - SearchQuery Reduce (SearchQuery expr); - } -} diff --git a/src/MailKit/Search/NumericSearchQuery.cs b/src/MailKit/Search/NumericSearchQuery.cs deleted file mode 100644 index 5bd0420..0000000 --- a/src/MailKit/Search/NumericSearchQuery.cs +++ /dev/null @@ -1,60 +0,0 @@ -// -// NumericSearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Search { - /// - /// A numeric search query. - /// - /// - /// A numeric search query. - /// - public class NumericSearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new numeric search query. - /// - /// The search term. - /// The numeric value. - public NumericSearchQuery (SearchTerm term, ulong value) : base (term) - { - Value = value; - } - - /// - /// Gets the numeric value to match against. - /// - /// - /// Gets the numeric value to match against. - /// - /// The numeric value. - public ulong Value { - get; private set; - } - } -} diff --git a/src/MailKit/Search/OrderBy.cs b/src/MailKit/Search/OrderBy.cs deleted file mode 100644 index 1d8637b..0000000 --- a/src/MailKit/Search/OrderBy.cs +++ /dev/null @@ -1,223 +0,0 @@ -// -// OrderBy.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search { - /// - /// Specifies a sort order for search results. - /// - /// - /// You can combine multiple rules to specify the sort - /// order that - /// should return the results in. - /// - public class OrderBy - { - /// - /// Initializes a new instance of the class. - /// - /// The field to sort by. - /// The sort order. - /// - /// cannot be . - /// - public OrderBy (OrderByType type, SortOrder order) - { - if (order == SortOrder.None) - throw new ArgumentOutOfRangeException (nameof (order)); - - Order = order; - Type = type; - } - - /// - /// Gets the field used for sorting. - /// - /// - /// Gets the field used for sorting. - /// - /// The field used for sorting. - public OrderByType Type { - get; private set; - } - - /// - /// Gets the sort order. - /// - /// - /// Gets the sort order. - /// - /// The sort order. - public SortOrder Order { - get; private set; - } - - /// - /// Sort results by arrival date in ascending order. - /// - /// - /// Sort results by arrival date in ascending order. - /// - public static readonly OrderBy Arrival = new OrderBy (OrderByType.Arrival, SortOrder.Ascending); - - /// - /// Sort results by arrival date in desending order. - /// - /// - /// Sort results by arrival date in desending order. - /// - public static readonly OrderBy ReverseArrival = new OrderBy (OrderByType.Arrival, SortOrder.Descending); - - /// - /// Sort results by the first email address in the Cc header in ascending order. - /// - /// - /// Sort results by the first email address in the Cc header in ascending order. - /// - public static readonly OrderBy Cc = new OrderBy (OrderByType.Cc, SortOrder.Ascending); - - /// - /// Sort results by the first email address in the Cc header in descending order. - /// - /// - /// Sort results by the first email address in the Cc header in descending order. - /// - public static readonly OrderBy ReverseCc = new OrderBy (OrderByType.Cc, SortOrder.Descending); - - /// - /// Sort results by the sent date in ascending order. - /// - /// - /// Sort results by the sent date in ascending order. - /// - public static readonly OrderBy Date = new OrderBy (OrderByType.Date, SortOrder.Ascending); - - /// - /// Sort results by the sent date in descending order. - /// - /// - /// Sort results by the sent date in descending order. - /// - public static readonly OrderBy ReverseDate = new OrderBy (OrderByType.Date, SortOrder.Descending); - - /// - /// Sort results by the first email address in the From header in ascending order. - /// - /// - /// Sort results by the first email address in the From header in ascending order. - /// - public static readonly OrderBy From = new OrderBy (OrderByType.From, SortOrder.Ascending); - - /// - /// Sort results by the first email address in the From header in descending order. - /// - /// - /// Sort results by the first email address in the From header in descending order. - /// - public static readonly OrderBy ReverseFrom = new OrderBy (OrderByType.From, SortOrder.Descending); - - /// - /// Sort results by the first display name in the From header in ascending order. - /// - /// - /// Sort results by the first display name in the From header in ascending order. - /// - public static readonly OrderBy DisplayFrom = new OrderBy (OrderByType.DisplayFrom, SortOrder.Ascending); - - /// - /// Sort results by the first display name in the From header in descending order. - /// - /// - /// Sort results by the first display name in the From header in descending order. - /// - public static readonly OrderBy ReverseDisplayFrom = new OrderBy (OrderByType.DisplayFrom, SortOrder.Descending); - - /// - /// Sort results by the message size in ascending order. - /// - /// - /// Sort results by the message size in ascending order. - /// - public static readonly OrderBy Size = new OrderBy (OrderByType.Size, SortOrder.Ascending); - - /// - /// Sort results by the message size in descending order. - /// - /// - /// Sort results by the message size in descending order. - /// - public static readonly OrderBy ReverseSize = new OrderBy (OrderByType.Size, SortOrder.Descending); - - /// - /// Sort results by the Subject header in ascending order. - /// - /// - /// Sort results by the Subject header in ascending order. - /// - public static readonly OrderBy Subject = new OrderBy (OrderByType.Subject, SortOrder.Ascending); - - /// - /// Sort results by the Subject header in descending order. - /// - /// - /// Sort results by the Subject header in descending order. - /// - public static readonly OrderBy ReverseSubject = new OrderBy (OrderByType.Subject, SortOrder.Descending); - - /// - /// Sort results by the first email address in the To header in ascending order. - /// - /// - /// Sort results by the first email address in the To header in ascending order. - /// - public static readonly OrderBy To = new OrderBy (OrderByType.To, SortOrder.Ascending); - - /// - /// Sort results by the first email address in the To header in descending order. - /// - /// - /// Sort results by the first email address in the To header in descending order. - /// - public static readonly OrderBy ReverseTo = new OrderBy (OrderByType.To, SortOrder.Descending); - - /// - /// Sort results by the first display name in the To header in ascending order. - /// - /// - /// Sort results by the first display name in the To header in ascending order. - /// - public static readonly OrderBy DisplayTo = new OrderBy (OrderByType.DisplayTo, SortOrder.Ascending); - - /// - /// Sort results by the first display name in the To header in descending order. - /// - /// - /// Sort results by the first display name in the To header in descending order. - /// - public static readonly OrderBy ReverseDisplayTo = new OrderBy (OrderByType.DisplayTo, SortOrder.Descending); - } -} diff --git a/src/MailKit/Search/OrderByAnnotation.cs b/src/MailKit/Search/OrderByAnnotation.cs deleted file mode 100644 index e662b33..0000000 --- a/src/MailKit/Search/OrderByAnnotation.cs +++ /dev/null @@ -1,91 +0,0 @@ -// -// OrderByAnnotation.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search { - /// - /// Specifies an annotation-based sort order for search results. - /// - /// - /// You can combine multiple rules to specify the sort - /// order that - /// should return the results in. - /// - public class OrderByAnnotation : OrderBy - { - /// - /// Initializes a new instance of the class. - /// - /// The annotation entry to sort by. - /// The annotation attribute to use for sorting. - /// The sort order. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid attribute for sorting. - /// - public OrderByAnnotation (AnnotationEntry entry, AnnotationAttribute attribute, SortOrder order) : base (OrderByType.Annotation, order) - { - if (entry == null) - throw new ArgumentNullException (nameof (entry)); - - if (attribute == null) - throw new ArgumentNullException (nameof (attribute)); - - if (attribute.Name != "value" || attribute.Scope == AnnotationScope.Both) - throw new ArgumentException ("Only the \"value.priv\" and \"value.shared\" attributes can be used for sorting.", nameof (attribute)); - - Entry = entry; - Attribute = attribute; - } - - /// - /// Get the annotation entry. - /// - /// - /// Gets the annotation entry. - /// - /// The annotation entry. - public AnnotationEntry Entry { - get; private set; - } - - /// - /// Get the annotation attribute. - /// - /// - /// Gets the annotation attribute. - /// - /// The annotation attribute. - public AnnotationAttribute Attribute { - get; private set; - } - } -} diff --git a/src/MailKit/Search/OrderByType.cs b/src/MailKit/Search/OrderByType.cs deleted file mode 100644 index bbb0508..0000000 --- a/src/MailKit/Search/OrderByType.cs +++ /dev/null @@ -1,90 +0,0 @@ -// -// OrderByType.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Search { - /// - /// The field to sort by. - /// - /// - /// The field to sort by. - /// - public enum OrderByType { - /// - /// Sort by an annotation value. - /// - Annotation, - - /// - /// Sort by the arrival date. - /// - Arrival, - - /// - /// Sort by the Cc header. - /// - Cc, - - /// - /// Sort by the Date header. - /// - Date, - - /// - /// Sort by the Display Name of the From header. - /// - DisplayFrom, - - /// - /// Sort by the Display Name of the To header. - /// - DisplayTo, - - /// - /// Sort by the From header. - /// - From, - - /// - /// Sort by the mod-sequence. - /// - ModSeq, - - /// - /// Sort by the message size. - /// - Size, - - /// - /// Sort by the message subject. - /// - Subject, - - /// - /// Sort by the To header. - /// - To - } -} diff --git a/src/MailKit/Search/SearchOptions.cs b/src/MailKit/Search/SearchOptions.cs deleted file mode 100644 index f1de5a2..0000000 --- a/src/MailKit/Search/SearchOptions.cs +++ /dev/null @@ -1,69 +0,0 @@ -// -// SearchOptions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search { - /// - /// Advanced search options. - /// - /// - /// Advanced search options. - /// - [Flags] - public enum SearchOptions { - /// - /// No options specified. - /// - None = 0, - - /// - /// Returns all of the matching unique identifiers. - /// - All = 1 << 0, - - /// - /// Returns the number of messages that match the search query. - /// - Count = 1 << 1, - - /// - /// Returns the minimum unique identifier of the messages that match the search query. - /// - Min = 1 << 2, - - /// - /// Returns the maximum unique identifier of the messages that match the search query. - /// - Max = 1 << 3, - - /// - /// Returns the relevancy scores of the messages that match the query. Can only be used - /// when using FUZZY search. - /// - Relevancy = 1 << 4 - } -} diff --git a/src/MailKit/Search/SearchQuery.cs b/src/MailKit/Search/SearchQuery.cs deleted file mode 100644 index 8ecf20c..0000000 --- a/src/MailKit/Search/SearchQuery.cs +++ /dev/null @@ -1,1136 +0,0 @@ -// -// SearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -namespace MailKit.Search { - /// - /// A specialized query for searching messages in a . - /// - /// - /// A specialized query for searching messages in a . - /// - public class SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new that matches all messages. - /// - public SearchQuery () : this (SearchTerm.All) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new with the specified search term. - /// - /// The search term. - protected SearchQuery (SearchTerm term) - { - Term = term; - } - - /// - /// Get the search term used by the search query. - /// - /// - /// Gets the search term used by the search query. - /// - /// The term. - public SearchTerm Term { - get; private set; - } - - /// - /// Match all messages in the folder. - /// - /// - /// Matches all messages in the folder. - /// - public static readonly SearchQuery All = new SearchQuery (SearchTerm.All); - - /// - /// Create a conditional AND operation. - /// - /// - /// A conditional AND operation only evaluates the second operand if the first operand evaluates to true. - /// - /// A representing the conditional AND operation. - /// The first operand. - /// The second operand. - /// - /// is null. - /// -or- - /// is null. - /// - public static BinarySearchQuery And (SearchQuery left, SearchQuery right) - { - if (left == null) - throw new ArgumentNullException (nameof (left)); - - if (right == null) - throw new ArgumentNullException (nameof (right)); - - return new BinarySearchQuery (SearchTerm.And, left, right); - } - - /// - /// Create a conditional AND operation. - /// - /// - /// A conditional AND operation only evaluates the second operand if the first operand evaluates to true. - /// - /// A representing the conditional AND operation. - /// An additional query to execute. - /// - /// is null. - /// - public BinarySearchQuery And (SearchQuery expr) - { - if (expr == null) - throw new ArgumentNullException (nameof (expr)); - - return new BinarySearchQuery (SearchTerm.And, this, expr); - } - - /// - /// Match messages with the specified annotation. - /// - /// - /// Matches messages with the specified annotation. - /// This feature is not supported by all IMAP servers. - /// - /// The annotation entry. - /// The annotation attribute. - /// The annotation attribute value. - /// A . - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid attribute for searching. - /// - public static AnnotationSearchQuery AnnotationsContain (AnnotationEntry entry, AnnotationAttribute attribute, string value) - { - return new AnnotationSearchQuery (entry, attribute, value); - } - - /// - /// Match messages with the flag set. - /// - /// - /// Matches messages with the flag set. - /// - public static readonly SearchQuery Answered = new SearchQuery (SearchTerm.Answered); - - /// - /// Match messages where the Bcc header contains the specified text. - /// - /// - /// Matches messages where the Bcc header contains the specified text. - /// - /// A . - /// The text to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery BccContains (string text) - { - return new TextSearchQuery (SearchTerm.BccContains, text); - } - - /// - /// Match messages where the message body contains the specified text. - /// - /// - /// Matches messages where the message body contains the specified text. - /// - /// A . - /// The text to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery BodyContains (string text) - { - return new TextSearchQuery (SearchTerm.BodyContains, text); - } - - /// - /// Match messages where the Cc header contains the specified text. - /// - /// - /// Matches messages where the Cc header contains the specified text. - /// - /// A . - /// The text to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery CcContains (string text) - { - return new TextSearchQuery (SearchTerm.CcContains, text); - } - - /// - /// Match messages that have mod-sequence values greater than or equal to the specified mod-sequence value. - /// - /// - /// Matches messages that have mod-sequence values greater than or equal to the specified mod-sequence value. - /// - /// A . - /// The mod-sequence value. - public static SearchQuery ChangedSince (ulong modseq) - { - return new NumericSearchQuery (SearchTerm.ModSeq, modseq); - } - - /// - /// Match messages with the flag set. - /// - /// - /// Matches messages with the flag set. - /// - public static readonly SearchQuery Deleted = new SearchQuery (SearchTerm.Deleted); - - /// - /// Match messages that were delivered after the specified date. - /// - /// - /// Matches messages that were delivered after the specified date. - /// The resolution of this search query does not include the time. - /// - /// A . - /// The date. - public static DateSearchQuery DeliveredAfter (DateTime date) - { - return new DateSearchQuery (SearchTerm.DeliveredAfter, date); - } - - /// - /// Match messages that were delivered before the specified date. - /// - /// - /// Matches messages that were delivered before the specified date. - /// The resolution of this search query does not include the time. - /// - /// A . - /// The date. - public static DateSearchQuery DeliveredBefore (DateTime date) - { - return new DateSearchQuery (SearchTerm.DeliveredBefore, date); - } - - /// - /// Match messages that were delivered on the specified date. - /// - /// - /// Matches messages that were delivered on the specified date. - /// The resolution of this search query does not include the time. - /// - /// A . - /// The date. - public static DateSearchQuery DeliveredOn (DateTime date) - { - return new DateSearchQuery (SearchTerm.DeliveredOn, date); - } - - /// - /// Match messages that do not have the specified custom flag set. - /// - /// - /// Matches messages that do not have the specified custom flag set. - /// - /// A . - /// The custom flag. - /// - /// is null. - /// - /// - /// is empty. - /// - [Obsolete ("Use NotKeyword() instead.")] - public static TextSearchQuery DoesNotHaveCustomFlag (string flag) - { - return NotKeyword (flag); - } - - /// - /// Match messages that do not have any of the specified custom flags set. - /// - /// - /// Matches messages that do not have any of the specified custom flags set. - /// - /// A . - /// The custom flags. - /// - /// is null. - /// - /// - /// One or more of the is null or empty. - /// -or- - /// No custom flags were given. - /// - [Obsolete ("Use NotKeywords() instead.")] - public static SearchQuery DoesNotHaveCustomFlags (IEnumerable flags) - { - return NotKeywords (flags); - } - - /// - /// Match messages that do not have any of the specified flags set. - /// - /// - /// Matches messages that do not have any of the specified flags set. - /// - /// A . - /// The message flags. - /// - /// does not specify any valid message flags. - /// - [Obsolete ("Use NotFlags() instead.")] - public static SearchQuery DoesNotHaveFlags (MessageFlags flags) - { - return NotFlags (flags); - } - - /// - /// Match messages with the flag set. - /// - /// - /// Matches messages with the flag set. - /// - public static readonly SearchQuery Draft = new SearchQuery (SearchTerm.Draft); - - /// - /// Match messages using a saved search filter. - /// - /// - /// Matches messages using a saved search filter. - /// - /// A . - /// The name of the saved search. - public static SearchQuery Filter (string name) - { - return new FilterSearchQuery (name); - } - - /// - /// Match messages using a saved search filter. - /// - /// - /// Matches messages using a saved search filter. - /// - /// A . - /// The name of the saved search. - public static SearchQuery Filter (MetadataTag filter) - { - return new FilterSearchQuery (filter); - } - - /// - /// Match messages with the flag set. - /// - /// - /// Matches messages with the flag set. - /// - public static readonly SearchQuery Flagged = new SearchQuery (SearchTerm.Flagged); - - /// - /// Match messages where the From header contains the specified text. - /// - /// - /// Matches messages where the From header contains the specified text. - /// - /// A . - /// The text to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery FromContains (string text) - { - return new TextSearchQuery (SearchTerm.FromContains, text); - } - - /// - /// Apply a fuzzy matching algorithm to the specified expression. - /// - /// - /// Applies a fuzzy matching algorithm to the specified expression. - /// This feature is not supported by all IMAP servers. - /// - /// A . - /// The expression - /// - /// is null. - /// - public static UnarySearchQuery Fuzzy (SearchQuery expr) - { - if (expr == null) - throw new ArgumentNullException (nameof (expr)); - - return new UnarySearchQuery (SearchTerm.Fuzzy, expr); - } - - /// - /// Match messages that have the specified custom flag set. - /// - /// - /// Matches messages that have the specified custom flag set. - /// - /// A . - /// The custom flag. - /// - /// is null. - /// - /// - /// is empty. - /// - [Obsolete ("Use HasKeyword() instead.")] - public static TextSearchQuery HasCustomFlag (string flag) - { - return HasKeyword (flag); - } - - /// - /// Match messages that have the specified custom flags set. - /// - /// - /// Matches messages that have the specified custom flags set. - /// - /// A . - /// The custom flags. - /// - /// is null. - /// - /// - /// One or more of the is null or empty. - /// -or- - /// No custom flags were given. - /// - [Obsolete ("Use HasKeywords() instead.")] - public static SearchQuery HasCustomFlags (IEnumerable flags) - { - return HasKeywords (flags); - } - - /// - /// Match messages that have the specified flags set. - /// - /// - /// Matches messages that have the specified flags set. - /// - /// A . - /// The message flags. - /// - /// does not specify any valid message flags. - /// - public static SearchQuery HasFlags (MessageFlags flags) - { - var list = new List (); - - if ((flags & MessageFlags.Seen) != 0) - list.Add (Seen); - if ((flags & MessageFlags.Answered) != 0) - list.Add (Answered); - if ((flags & MessageFlags.Flagged) != 0) - list.Add (Flagged); - if ((flags & MessageFlags.Deleted) != 0) - list.Add (Deleted); - if ((flags & MessageFlags.Draft) != 0) - list.Add (Draft); - if ((flags & MessageFlags.Recent) != 0) - list.Add (Recent); - - if (list.Count == 0) - throw new ArgumentException ("No flags specified.", nameof (flags)); - - var query = list[0]; - for (int i = 1; i < list.Count; i++) - query = query.And (list[i]); - - return query; - } - - /// - /// Match messages that do not have any of the specified flags set. - /// - /// - /// Matches messages that do not have any of the specified flags set. - /// - /// A . - /// The message flags. - /// - /// does not specify any valid message flags. - /// - public static SearchQuery NotFlags (MessageFlags flags) - { - var list = new List (); - - if ((flags & MessageFlags.Seen) != 0) - list.Add (NotSeen); - if ((flags & MessageFlags.Answered) != 0) - list.Add (NotAnswered); - if ((flags & MessageFlags.Flagged) != 0) - list.Add (NotFlagged); - if ((flags & MessageFlags.Deleted) != 0) - list.Add (NotDeleted); - if ((flags & MessageFlags.Draft) != 0) - list.Add (NotDraft); - if ((flags & MessageFlags.Recent) != 0) - list.Add (NotRecent); - - if (list.Count == 0) - throw new ArgumentException ("No flags specified.", nameof (flags)); - - var query = list[0]; - for (int i = 1; i < list.Count; i++) - query = query.And (list[i]); - - return query; - } - - /// - /// Match messages that have the specified keyword set. - /// - /// - /// Matches messages that have the specified keyword set. - /// A keyword is a user-defined message flag. - /// - /// A . - /// The keyword. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery HasKeyword (string keyword) - { - if (keyword == null) - throw new ArgumentNullException (nameof (keyword)); - - if (keyword.Length == 0) - throw new ArgumentException ("The keyword cannot be an empty string.", nameof (keyword)); - - return new TextSearchQuery (SearchTerm.Keyword, keyword); - } - - /// - /// Match messages that have all of the specified keywords set. - /// - /// - /// Matches messages that have all of the specified keywords set. - /// A keyword is a user-defined message flag. - /// - /// A . - /// The keywords. - /// - /// is null. - /// - /// - /// One or more of the is null or empty. - /// -or- - /// No keywords were given. - /// - public static SearchQuery HasKeywords (IEnumerable keywords) - { - if (keywords == null) - throw new ArgumentNullException (nameof (keywords)); - - var list = new List (); - - foreach (var keyword in keywords) { - if (string.IsNullOrEmpty (keyword)) - throw new ArgumentException ("Cannot search for null or empty keywords.", nameof (keywords)); - - list.Add (new TextSearchQuery (SearchTerm.Keyword, keyword)); - } - - if (list.Count == 0) - throw new ArgumentException ("No keywords specified.", nameof (keywords)); - - var query = list[0]; - for (int i = 1; i < list.Count; i++) - query = query.And (list[i]); - - return query; - } - - /// - /// Match messages that do not have the specified keyword set. - /// - /// - /// Matches messages that do not have the specified keyword set. - /// A keyword is a user-defined message flag. - /// - /// A . - /// The keyword. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery NotKeyword (string keyword) - { - if (keyword == null) - throw new ArgumentNullException (nameof (keyword)); - - if (keyword.Length == 0) - throw new ArgumentException ("The keyword cannot be an empty string.", nameof (keyword)); - - return new TextSearchQuery (SearchTerm.NotKeyword, keyword); - } - - /// - /// Match messages that do not have any of the specified keywords set. - /// - /// - /// Matches messages that do not have any of the specified keywords set. - /// A keyword is a user-defined message flag. - /// - /// A . - /// The keywords. - /// - /// is null. - /// - /// - /// One or more of the is null or empty. - /// -or- - /// No keywords were given. - /// - public static SearchQuery NotKeywords (IEnumerable keywords) - { - if (keywords == null) - throw new ArgumentNullException (nameof (keywords)); - - var list = new List (); - - foreach (var keyword in keywords) { - if (string.IsNullOrEmpty (keyword)) - throw new ArgumentException ("Cannot search for null or empty keywords.", nameof (keywords)); - - list.Add (new TextSearchQuery (SearchTerm.NotKeyword, keyword)); - } - - if (list.Count == 0) - throw new ArgumentException ("No flags specified.", nameof (keywords)); - - var query = list[0]; - for (int i = 1; i < list.Count; i++) - query = query.And (list[i]); - - return query; - } - - /// - /// Match messages where the specified header contains the specified text. - /// - /// - /// Matches messages where the specified header contains the specified text. - /// - /// A . - /// The header field to match against. - /// The text to match against. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - public static HeaderSearchQuery HeaderContains (string field, string text) - { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - if (field.Length == 0) - throw new ArgumentException ("Cannot search an empty header field name.", nameof (field)); - - if (text == null) - throw new ArgumentNullException (nameof (text)); - - return new HeaderSearchQuery (field, text); - } - - /// - /// Match messages that are larger than the specified number of octets. - /// - /// - /// Matches messages that are larger than the specified number of octets. - /// - /// A . - /// The number of octets. - /// - /// is a negative value. - /// - public static NumericSearchQuery LargerThan (int octets) - { - if (octets < 0) - throw new ArgumentOutOfRangeException (nameof (octets)); - - return new NumericSearchQuery (SearchTerm.LargerThan, (ulong) octets); - } - - /// - /// Match messages where the raw message contains the specified text. - /// - /// - /// Matches messages where the raw message contains the specified text. - /// - /// A . - /// The text to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery MessageContains (string text) - { - return new TextSearchQuery (SearchTerm.MessageContains, text); - } - - /// - /// Match messages with the flag set but not the . - /// - /// - /// Matches messages with the flag set but not the . - /// - public static readonly SearchQuery New = new SearchQuery (SearchTerm.New); - - /// - /// Create a logical negation of the specified expression. - /// - /// - /// Creates a logical negation of the specified expression. - /// - /// A . - /// The expression - /// - /// is null. - /// - public static UnarySearchQuery Not (SearchQuery expr) - { - if (expr == null) - throw new ArgumentNullException (nameof (expr)); - - return new UnarySearchQuery (SearchTerm.Not, expr); - } - - /// - /// Match messages that do not have the flag set. - /// - /// - /// Matches messages that do not have the flag set. - /// - public static readonly SearchQuery NotAnswered = new SearchQuery (SearchTerm.NotAnswered); - - /// - /// Match messages that do not have the flag set. - /// - /// - /// Matches messages that do not have the flag set. - /// - public static readonly SearchQuery NotDeleted = new SearchQuery (SearchTerm.NotDeleted); - - /// - /// Match messages that do not have the flag set. - /// - /// - /// Matches messages that do not have the flag set. - /// - public static readonly SearchQuery NotDraft = new SearchQuery (SearchTerm.NotDraft); - - /// - /// Match messages that do not have the flag set. - /// - /// - /// Matches messages that do not have the flag set. - /// - public static readonly SearchQuery NotFlagged = new SearchQuery (SearchTerm.NotFlagged); - - /// - /// Match messages that do not have the flag set. - /// - /// - /// Matches messages that do not have the flag set. - /// - public static readonly SearchQuery NotRecent = new SearchQuery (SearchTerm.NotRecent); - - /// - /// Match messages that do not have the flag set. - /// - /// - /// Matches messages that do not have the flag set. - /// - public static readonly SearchQuery NotSeen = new SearchQuery (SearchTerm.NotSeen); - - /// - /// Match messages that do not have the flag set. - /// - /// - /// Matches messages that do not have the flag set. - /// - public static readonly SearchQuery Old = new SearchQuery (SearchTerm.NotRecent); - - /// - /// Match messages older than the specified number of seconds. - /// - /// - /// Matches messages older than the specified number of seconds. - /// - /// A . - /// The number of seconds. - /// - /// The number of seconds cannot be less than 1. - /// - public static NumericSearchQuery OlderThan (int seconds) - { - if (seconds < 1) - throw new ArgumentOutOfRangeException (nameof (seconds)); - - return new NumericSearchQuery (SearchTerm.Older, (ulong) seconds); - } - - /// - /// Create a conditional OR operation. - /// - /// - /// A conditional OR operation only evaluates the second operand if the first operand evaluates to false. - /// - /// A representing the conditional OR operation. - /// The first operand. - /// The second operand. - /// - /// is null. - /// -or- - /// is null. - /// - public static BinarySearchQuery Or (SearchQuery left, SearchQuery right) - { - if (left == null) - throw new ArgumentNullException (nameof (left)); - - if (right == null) - throw new ArgumentNullException (nameof (right)); - - return new BinarySearchQuery (SearchTerm.Or, left, right); - } - - /// - /// Create a conditional OR operation. - /// - /// - /// A conditional OR operation only evaluates the second operand if the first operand evaluates to true. - /// - /// A representing the conditional AND operation. - /// An additional query to execute. - /// - /// is null. - /// - public BinarySearchQuery Or (SearchQuery expr) - { - if (expr == null) - throw new ArgumentNullException (nameof (expr)); - - return new BinarySearchQuery (SearchTerm.Or, this, expr); - } - - /// - /// Match messages with the flag set. - /// - /// - /// Matches messages with the flag set. - /// - public static readonly SearchQuery Recent = new SearchQuery (SearchTerm.Recent); - - /// - /// Match messages with the flag set. - /// - /// - /// Matches messages with the flag set. - /// - public static readonly SearchQuery Seen = new SearchQuery (SearchTerm.Seen); - - /// - /// Match messages that were sent on or after the specified date. - /// - /// - /// Matches messages that were sent on or after the specified date. - /// The resolution of this search query does not include the time. - /// - /// A . - /// The date. - [Obsolete ("Use SentSince (DateTime)")] - public static DateSearchQuery SentAfter (DateTime date) - { - return SentSince (date); - } - - /// - /// Match messages that were sent before the specified date. - /// - /// - /// Matches messages that were sent before the specified date. - /// The resolution of this search query does not include the time. - /// - /// A . - /// The date. - public static DateSearchQuery SentBefore (DateTime date) - { - return new DateSearchQuery (SearchTerm.SentBefore, date); - } - - /// - /// Match messages that were sent on the specified date. - /// - /// - /// Matches messages that were sent on the specified date. - /// The resolution of this search query does not include the time. - /// - /// A . - /// The date. - public static DateSearchQuery SentOn (DateTime date) - { - return new DateSearchQuery (SearchTerm.SentOn, date); - } - - /// - /// Match messages that were sent since the specified date. - /// - /// - /// Matches messages that were sent since the specified date. - /// The resolution of this search query does not include the time. - /// - /// A . - /// The date. - public static DateSearchQuery SentSince (DateTime date) - { - return new DateSearchQuery (SearchTerm.SentSince, date); - } - - /// - /// Match messages that are smaller than the specified number of octets. - /// - /// - /// Matches messages that are smaller than the specified number of octets. - /// - /// A . - /// The number of octets. - /// - /// is a negative value. - /// - public static NumericSearchQuery SmallerThan (int octets) - { - if (octets < 0) - throw new ArgumentOutOfRangeException (nameof (octets)); - - return new NumericSearchQuery (SearchTerm.SmallerThan, (ulong) octets); - } - - /// - /// Match messages where the Subject header contains the specified text. - /// - /// - /// Matches messages where the Subject header contains the specified text. - /// - /// A . - /// The text to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery SubjectContains (string text) - { - return new TextSearchQuery (SearchTerm.SubjectContains, text); - } - - /// - /// Match messages where the To header contains the specified text. - /// - /// - /// Matches messages where the To header contains the specified text. - /// - /// A . - /// The text to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery ToContains (string text) - { - return new TextSearchQuery (SearchTerm.ToContains, text); - } - - /// - /// Limit the search query to messages with the specified unique identifiers. - /// - /// - /// Limits the search query to messages with the specified unique identifiers. - /// - /// A . - /// The unique identifiers. - /// - /// is null. - /// - /// - /// is empty. - /// - public static UidSearchQuery Uids (IList uids) - { - return new UidSearchQuery (uids); - } - - /// - /// Match messages younger than the specified number of seconds. - /// - /// - /// Matches messages younger than the specified number of seconds. - /// - /// A . - /// The number of seconds. - /// - /// The number of seconds cannot be less than 1. - /// - public static NumericSearchQuery YoungerThan (int seconds) - { - if (seconds < 1) - throw new ArgumentOutOfRangeException (nameof (seconds)); - - return new NumericSearchQuery (SearchTerm.Younger, (ulong) seconds); - } - - #region GMail extensions - - /// - /// Match messages that have the specified GMail message identifier. - /// - /// - /// This search term can only be used with GMail. - /// - /// A . - /// The GMail message identifier. - public static NumericSearchQuery GMailMessageId (ulong id) - { - return new NumericSearchQuery (SearchTerm.GMailMessageId, id); - } - - /// - /// Match messages belonging to the specified GMail thread. - /// - /// - /// This search term can only be used with GMail. - /// - /// A . - /// The GMail thread. - public static NumericSearchQuery GMailThreadId (ulong thread) - { - return new NumericSearchQuery (SearchTerm.GMailThreadId, thread); - } - - /// - /// Match messages that have the specified GMail label. - /// - /// - /// This search term can only be used with GMail. - /// - /// A . - /// The GMail label. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery HasGMailLabel (string label) - { - if (label == null) - throw new ArgumentNullException (nameof (label)); - - if (label.Length == 0) - throw new ArgumentException ("Cannot search for an empty string.", nameof (label)); - - return new TextSearchQuery (SearchTerm.GMailLabels, label); - } - - /// - /// Match messages using the GMail search expression. - /// - /// - /// This search term can only be used with GMail. - /// - /// A . - /// The raw GMail search text. - /// - /// is null. - /// - /// - /// is empty. - /// - public static TextSearchQuery GMailRawSearch (string expression) - { - if (expression == null) - throw new ArgumentNullException (nameof (expression)); - - if (expression.Length == 0) - throw new ArgumentException ("Cannot search for an empty string.", nameof (expression)); - - return new TextSearchQuery (SearchTerm.GMailRaw, expression); - } - - #endregion - - internal virtual SearchQuery Optimize (ISearchQueryOptimizer optimizer) - { - return optimizer.Reduce (this); - } - } -} diff --git a/src/MailKit/Search/SearchResults.cs b/src/MailKit/Search/SearchResults.cs deleted file mode 100644 index d50a920..0000000 --- a/src/MailKit/Search/SearchResults.cs +++ /dev/null @@ -1,116 +0,0 @@ -// -// SearchResults.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Collections.Generic; - -namespace MailKit.Search { - /// - /// The results of a search. - /// - /// - /// The results of a search. - /// - public class SearchResults - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The sort-order to use for the unique identifiers. - public SearchResults (SortOrder order = SortOrder.None) - { - UniqueIds = new UniqueIdSet (order); - } - - /// - /// Get or set the unique identifiers of the messages that matched the search query. - /// - /// - /// Gets or sets the unique identifiers of the messages that matched the search query. - /// - /// The unique identifiers. - public IList UniqueIds { - get; set; - } - - /// - /// Get or set the number of messages that matched the search query. - /// - /// - /// Gets or sets the number of messages that matched the search query. - /// - /// The count. - public int Count { - get; set; - } - - /// - /// Get or set the minimum unique identifier that matched the search query. - /// - /// - /// Gets or sets the minimum unique identifier that matched the search query. - /// - /// The minimum unique identifier. - public UniqueId? Min { - get; set; - } - - /// - /// Get or set the maximum unique identifier that matched the search query. - /// - /// - /// Gets or sets the maximum unique identifier that matched the search query. - /// - /// The maximum unique identifier. - public UniqueId? Max { - get; set; - } - - /// - /// Gets or sets the mod-sequence identifier of the messages that matched the search query. - /// - /// - /// Gets or sets the mod-sequence identifier of the messages that matched the search query. - /// - /// The mod-sequence identifier. - public ulong? ModSeq { - get; set; - } - - /// - /// Gets or sets the relevancy scores of the messages that matched the search query. - /// - /// - /// Gets or sets the relevancy scores of the messages that matched the search query. - /// - /// The relevancy scores. - public IList Relevancy { - get; set; - } - } -} diff --git a/src/MailKit/Search/SearchTerm.cs b/src/MailKit/Search/SearchTerm.cs deleted file mode 100644 index 1a12f74..0000000 --- a/src/MailKit/Search/SearchTerm.cs +++ /dev/null @@ -1,288 +0,0 @@ -// -// SearchTerm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Search { - /// - /// A search term. - /// - /// - /// The search term as used by . - /// - public enum SearchTerm { - /// - /// A search term that matches all messages. - /// - All, - - /// - /// A search term that logically combines 2 or more other - /// search expressions such that messages must match both - /// expressions. - /// - And, - - /// - /// A search term that matches messages that have the specified annotation. - /// - Annotation, - - /// - /// A search term that matches answered messages. - /// - Answered, - - /// - /// A search term that matches messages that contain a specified - /// string within the Bcc header. - /// - BccContains, - - /// - /// A search term that matches messages that contain a specified - /// string within the body of the message. - /// - BodyContains, - - /// - /// A search term that matches messages that contain a specified - /// string within the Cc header. - /// - CcContains, - - /// - /// A search term that matches deleted messages. - /// - Deleted, - - /// - /// A search term that matches messages delivered after a specified date. - /// - DeliveredAfter, - - /// - /// A search term that matches messages delivered before a specified date. - /// - DeliveredBefore, - - /// - /// A search term that matches messages delivered on a specified date. - /// - DeliveredOn, - - /// - /// A search term that matches draft messages. - /// - Draft, - - /// - /// A search term that makes use of a predefined filter. - /// - Filter, - - /// - /// A search term that matches flagged messages. - /// - Flagged, - - /// - /// A search term that matches messages that contain a specified - /// string within the From header. - /// - FromContains, - - /// - /// A search term that modifies another search expression to allow - /// fuzzy matching. - /// - Fuzzy, - - /// - /// A search term that matches messages that contain a specified - /// string within a particular header. - /// - HeaderContains, - - /// - /// A search term that matches messages that contain a specified - /// keyword. - /// - Keyword, - - /// - /// A search term that matches messages that are larger than a - /// specified number of bytes. - /// - LargerThan, - - /// - /// A search term that matches messages that contain a specified - /// string anywhere within the message. - /// - MessageContains, - - /// - /// A search term that matches messages that have the specified - /// modification sequence value. - /// - ModSeq, - - /// - /// A search term that matches new messages. - /// - New, - - /// - /// A search term that modifies another search expression such that - /// messages must match the logical inverse of the expression. - /// - Not, - - /// - /// A search term that matches messages that have not been answered. - /// - NotAnswered, - - /// - /// A search term that matches messages that have not been deleted. - /// - NotDeleted, - - /// - /// A search term that matches messages that are not drafts. - /// - NotDraft, - - /// - /// A search term that matches messages that have not been flagged. - /// - NotFlagged, - - /// - /// A search term that matches messages that do not contain a specified - /// keyword. - /// - NotKeyword, - - /// - /// A search term that matches messages that are not recent. - /// - NotRecent, - - /// - /// A search term that matches messages that have not been seen. - /// - NotSeen, - - /// - /// A search term that matches messages that are older than a specified date. - /// - Older, - - /// - /// A search term that logically combines 2 or more other - /// search expressions such that messages only need to match - /// one of the expressions. - /// - Or, - - /// - /// A search term that matches messages that are recent. - /// - Recent, - - /// - /// A search term that matches messages that have been seen. - /// - Seen, - - /// - /// A search term that matches messages that were sent before a specified date. - /// - SentBefore, - - /// - /// A search term that matches messages that were sent on a specified date. - /// - SentOn, - - /// - /// A search term that matches messages that were sent since a specified date. - /// - SentSince, - - /// - /// A search term that matches messages that are smaller than a - /// specified number of bytes. - /// - SmallerThan, - - /// - /// A search term that matches messages that contain a specified - /// string within the Subject header. - /// - SubjectContains, - - /// - /// A search term that matches messages that contain a specified - /// string within the To header. - /// - ToContains, - - /// - /// A search term that matches messages included within a specified - /// set of unique identifiers. - /// - Uid, - - /// - /// A search term that matches messages that are younger than a specified date. - /// - Younger, - - // GMail SEARCH extensions - - /// - /// A search term that matches messages with a specified GMail message identifier. - /// - GMailMessageId, - - /// - /// A search term that matches messages with a specified GMail thread (conversation) - /// identifier. - /// - GMailThreadId, - - /// - /// A search term that matches messages with the specified GMail labels. - /// - GMailLabels, - - /// - /// A search term that uses the GMail search syntax. - /// - GMailRaw, - } -} diff --git a/src/MailKit/Search/SortOrder.cs b/src/MailKit/Search/SortOrder.cs deleted file mode 100644 index d07f4f8..0000000 --- a/src/MailKit/Search/SortOrder.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// SortOrder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Search { - /// - /// An enumeration of sort orders. - /// - /// - /// An enumeration of sort orders. - /// - public enum SortOrder { - /// - /// No sorting order. - /// - None, - - /// - /// Sort in ascending order. - /// - Ascending, - - /// - /// Sort in descending order. - /// - Descending - } -} diff --git a/src/MailKit/Search/TextSearchQuery.cs b/src/MailKit/Search/TextSearchQuery.cs deleted file mode 100644 index 2bc2812..0000000 --- a/src/MailKit/Search/TextSearchQuery.cs +++ /dev/null @@ -1,75 +0,0 @@ -// -// TextSearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search -{ - /// - /// A text-based search query. - /// - /// - /// A text-based search query. - /// - public class TextSearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new text-based search query. - /// - /// The search term. - /// The text to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public TextSearchQuery (SearchTerm term, string text) : base (term) - { - if (text == null) - throw new ArgumentNullException (nameof (text)); - - if (text.Length == 0) - throw new ArgumentException ("Cannot search for an empty string.", nameof (text)); - - Text = text; - } - - /// - /// Gets the text to match against. - /// - /// - /// Gets the text to match against. - /// - /// The text. - public string Text { - get; private set; - } - } -} diff --git a/src/MailKit/Search/UidSearchQuery.cs b/src/MailKit/Search/UidSearchQuery.cs deleted file mode 100644 index 6dee25b..0000000 --- a/src/MailKit/Search/UidSearchQuery.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// UidSearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -namespace MailKit.Search -{ - /// - /// A unique identifier-based search query. - /// - /// - /// A unique identifier-based search query. - /// - public class UidSearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new unique identifier-based search query. - /// - /// The unique identifiers to match against. - /// - /// is null. - /// - /// - /// is empty. - /// - public UidSearchQuery (IList uids) : base (SearchTerm.Uid) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (uids.Count == 0) - throw new ArgumentException ("Cannot search for an empty set of unique identifiers.", nameof (uids)); - - Uids = uids; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new unique identifier-based search query. - /// - /// The unique identifier to match against. - /// - /// is an invalid unique identifier. - /// - public UidSearchQuery (UniqueId uid) : base (SearchTerm.Uid) - { - if (!uid.IsValid) - throw new ArgumentException ("Cannot search for an invalid unique identifier.", nameof (uid)); - - Uids = new UniqueIdSet (SortOrder.Ascending); - Uids.Add (uid); - } - - /// - /// Gets the unique identifiers to match against. - /// - /// - /// Gets the unique identifiers to match against. - /// - /// The unique identifiers. - public new IList Uids { - get; private set; - } - } -} diff --git a/src/MailKit/Search/UnarySearchQuery.cs b/src/MailKit/Search/UnarySearchQuery.cs deleted file mode 100644 index 667bf53..0000000 --- a/src/MailKit/Search/UnarySearchQuery.cs +++ /dev/null @@ -1,82 +0,0 @@ -// -// UnarySearchQuery.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Search -{ - /// - /// A unary search query such as a NOT expression. - /// - /// - /// A unary search query such as a NOT expression. - /// - public class UnarySearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new unary search query. - /// - /// The search term. - /// The operand. - /// - /// is null. - /// - public UnarySearchQuery (SearchTerm term, SearchQuery operand) : base (term) - { - if (operand == null) - throw new ArgumentNullException (nameof (operand)); - - Operand = operand; - } - - /// - /// Gets the inner operand. - /// - /// - /// Gets the inner operand. - /// - /// The operand. - public SearchQuery Operand { - get; private set; - } - - internal override SearchQuery Optimize (ISearchQueryOptimizer optimizer) - { - var operand = Operand.Optimize (optimizer); - SearchQuery unary; - - if (operand != Operand) - unary = new UnarySearchQuery (Term, operand); - else - unary = this; - - return optimizer.Reduce (unary); - } - } -} diff --git a/src/MailKit/Security/AuthenticationException.cs b/src/MailKit/Security/AuthenticationException.cs deleted file mode 100644 index 357b9d2..0000000 --- a/src/MailKit/Security/AuthenticationException.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// AuthenticationException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Runtime.Serialization; -#endif - -namespace MailKit.Security { - /// - /// The exception that is thrown when there is an authentication error. - /// - /// - /// The exception that is thrown when there is an authentication error. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class AuthenticationException : Exception - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the seriaized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - protected AuthenticationException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// An inner exception. - public AuthenticationException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public AuthenticationException (string message) : base (message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public AuthenticationException () : base ("Authentication failed.") - { - } - } -} diff --git a/src/MailKit/Security/KeyedHashAlgorithm.cs b/src/MailKit/Security/KeyedHashAlgorithm.cs deleted file mode 100644 index 97e46f2..0000000 --- a/src/MailKit/Security/KeyedHashAlgorithm.cs +++ /dev/null @@ -1,137 +0,0 @@ -// -// KeyedHashAlgorithm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2019 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using Windows.Storage.Streams; -using Windows.Security.Cryptography; -using Windows.Security.Cryptography.Core; - -namespace MailKit.Security { - /// - /// A keyed hash algorithm. - /// - /// - /// A keyed hash algorithm. - /// - public abstract class KeyedHashAlgorithm : IDisposable - { - CryptographicHash hmac; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new keyed hash algorithm context. - /// - /// The MAC algorithm name. - /// The secret key. - protected KeyedHashAlgorithm (string algorithm, byte[] key) - { - var mac = MacAlgorithmProvider.OpenAlgorithm (algorithm); - var buf = CryptographicBuffer.CreateFromByteArray (key); - hmac = mac.CreateHash (buf); - } - - /// - /// Computes the hash code for the buffer. - /// - /// - /// Computes the hash code for the buffer. - /// - /// The computed hash code. - /// The buffer. - /// - /// is null. - /// - /// - /// The keyed hash algorithm context has been disposed. - /// - public byte[] ComputeHash (byte[] buffer) - { - if (buffer == null) - throw new ArgumentNullException ("data"); - - hmac.Append (CryptographicBuffer.CreateFromByteArray (buffer)); - var value = hmac.GetValueAndReset (); - byte[] hash; - - CryptographicBuffer.CopyToByteArray (value, out hash); - - return hash; - } - - /// - /// Releases all resource used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After calling - /// , you must release all references to the so the - /// garbage collector can reclaim the memory that the was occupying. - public void Dispose () - { - } - } - - /// - /// The HMAC SHA-1 algorithm. - /// - /// - /// The HMAC SHA-1 algorithm. - /// - public class HMACSHA1 : KeyedHashAlgorithm - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new HMAC SHA-1 context. - /// - /// The secret key. - public HMACSHA1 (byte[] key) : base (MacAlgorithmNames.HmacSha1, key) - { - } - } - - /// - /// The HMAC SHA-256 algorithm. - /// - /// - /// The HMAC SHA-256 algorithm. - /// - public class HMACSHA256 : KeyedHashAlgorithm - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new HMAC SHA-256 context. - /// - /// The secret key. - public HMACSHA256 (byte[] key) : base (MacAlgorithmNames.HmacSha256, key) - { - } - } -} diff --git a/src/MailKit/Security/Ntlm/BitConverterLE.cs b/src/MailKit/Security/Ntlm/BitConverterLE.cs deleted file mode 100644 index 4db415b..0000000 --- a/src/MailKit/Security/Ntlm/BitConverterLE.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// Mono.Security.BitConverterLE.cs -// Like System.BitConverter but always little endian -// -// Author: Bernie Solomon -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; - -namespace MailKit.Security.Ntlm -{ - sealed class BitConverterLE - { - BitConverterLE () - { - } - - unsafe static byte[] GetULongBytes (byte *bytes) - { - if (BitConverter.IsLittleEndian) - return new [] { bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] }; - - return new [] { bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0] }; - } - - unsafe internal static byte[] GetBytes (long value) - { - return GetULongBytes ((byte *) &value); - } - - unsafe static void UShortFromBytes (byte *dst, byte[] src, int startIndex) - { - if (BitConverter.IsLittleEndian) { - dst[0] = src[startIndex]; - dst[1] = src[startIndex + 1]; - } else { - dst[0] = src[startIndex + 1]; - dst[1] = src[startIndex]; - } - } - - unsafe static void UIntFromBytes (byte *dst, byte[] src, int startIndex) - { - if (BitConverter.IsLittleEndian) { - dst[0] = src[startIndex]; - dst[1] = src[startIndex + 1]; - dst[2] = src[startIndex + 2]; - dst[3] = src[startIndex + 3]; - } else { - dst[0] = src[startIndex + 3]; - dst[1] = src[startIndex + 2]; - dst[2] = src[startIndex + 1]; - dst[3] = src[startIndex]; - } - } - - unsafe internal static short ToInt16 (byte[] value, int startIndex) - { - short ret; - - UShortFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static int ToInt32 (byte[] value, int startIndex) - { - int ret; - - UIntFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static ushort ToUInt16 (byte[] value, int startIndex) - { - ushort ret; - - UShortFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - - unsafe internal static uint ToUInt32 (byte[] value, int startIndex) - { - uint ret; - - UIntFromBytes ((byte *) &ret, value, startIndex); - - return ret; - } - } -} diff --git a/src/MailKit/Security/Ntlm/ChallengeResponse2.cs b/src/MailKit/Security/Ntlm/ChallengeResponse2.cs deleted file mode 100644 index 06832c9..0000000 --- a/src/MailKit/Security/Ntlm/ChallengeResponse2.cs +++ /dev/null @@ -1,280 +0,0 @@ -// -// Mono.Security.Protocol.Ntlm.ChallengeResponse -// Implements Challenge Response for NTLM v1 and NTLM v2 Session -// -// Authors: Sebastien Pouliot -// Martin Baulig -// Jeffrey Stedfast -// -// Copyright (c) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (c) 2004 Novell (http://www.novell.com) -// Copyright (c) 2012 Xamarin, Inc. (http://www.xamarin.com) -// -// References -// a. NTLM Authentication Scheme for HTTP, Ronald Tschalär -// http://www.innovation.ch/java/ntlm.html -// b. The NTLM Authentication Protocol, Copyright © 2003 Eric Glass -// http://davenport.sourceforge.net/ntlm.html -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Text; -using System.Security.Cryptography; - -namespace MailKit.Security.Ntlm { - static class ChallengeResponse2 - { - static readonly byte[] Magic = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; - - // This is the pre-encrypted magic value with a null DES key (0xAAD3B435B51404EE) - // Ref: http://packetstormsecurity.nl/Crackers/NT/l0phtcrack/l0phtcrack2.5-readme.html - static readonly byte[] NullEncMagic = { 0xAA, 0xD3, 0xB4, 0x35, 0xB5, 0x14, 0x04, 0xEE }; - - static byte[] ComputeLM (string password, byte[] challenge) - { - var buffer = new byte[21]; - - // create Lan Manager password - using (var des = DES.Create ()) { - des.Mode = CipherMode.ECB; - - // Note: In .NET DES cannot accept a weak key - // this can happen for a null password - if (string.IsNullOrEmpty (password)) { - Buffer.BlockCopy (NullEncMagic, 0, buffer, 0, 8); - } else { - des.Key = PasswordToKey (password, 0); - using (var ct = des.CreateEncryptor ()) - ct.TransformBlock (Magic, 0, 8, buffer, 0); - } - - // and if a password has less than 8 characters - if (password == null || password.Length < 8) { - Buffer.BlockCopy (NullEncMagic, 0, buffer, 8, 8); - } else { - des.Key = PasswordToKey (password, 7); - using (var ct = des.CreateEncryptor ()) - ct.TransformBlock (Magic, 0, 8, buffer, 8); - } - } - - return GetResponse (challenge, buffer); - } - - static byte[] ComputeNtlmPassword (string password) - { - var buffer = new byte[21]; - - // create NT password - using (var md4 = new MD4 ()) { - var data = password == null ? new byte[0] : Encoding.Unicode.GetBytes (password); - var hash = md4.ComputeHash (data); - Buffer.BlockCopy (hash, 0, buffer, 0, 16); - - // clean up - Array.Clear (data, 0, data.Length); - Array.Clear (hash, 0, hash.Length); - } - - return buffer; - } - - static byte[] ComputeNtlm (string password, byte[] challenge) - { - var buffer = ComputeNtlmPassword (password); - return GetResponse (challenge, buffer); - } - - static void ComputeNtlmV2Session (string password, byte[] challenge, out byte[] lm, out byte[] ntlm) - { - var nonce = new byte[8]; - - using (var rng = RandomNumberGenerator.Create ()) - rng.GetBytes (nonce); - - var sessionNonce = new byte[challenge.Length + 8]; - challenge.CopyTo (sessionNonce, 0); - nonce.CopyTo (sessionNonce, challenge.Length); - - lm = new byte[24]; - nonce.CopyTo (lm, 0); - - using (var md5 = MD5.Create ()) { - var hash = md5.ComputeHash (sessionNonce); - var newChallenge = new byte[8]; - - Array.Copy (hash, newChallenge, 8); - - ntlm = ComputeNtlm (password, newChallenge); - - // clean up - Array.Clear (newChallenge, 0, newChallenge.Length); - Array.Clear (hash, 0, hash.Length); - } - - // clean up - Array.Clear (sessionNonce, 0, sessionNonce.Length); - Array.Clear (nonce, 0, nonce.Length); - } - - static byte[] ComputeNtlmV2 (Type2Message type2, string username, string password, string domain) - { - var ntlm_hash = ComputeNtlmPassword (password); - - var ubytes = Encoding.Unicode.GetBytes (username.ToUpperInvariant ()); - var tbytes = Encoding.Unicode.GetBytes (domain); - - var bytes = new byte[ubytes.Length + tbytes.Length]; - ubytes.CopyTo (bytes, 0); - Array.Copy (tbytes, 0, bytes, ubytes.Length, tbytes.Length); - - byte[] ntlm_v2_hash; - - using (var md5 = new HMACMD5 (ntlm_hash)) - ntlm_v2_hash = md5.ComputeHash (bytes); - - Array.Clear (ntlm_hash, 0, ntlm_hash.Length); - - using (var md5 = new HMACMD5 (ntlm_v2_hash)) { - var timestamp = DateTime.Now.Ticks - 504911232000000000; - var nonce = new byte[8]; - - using (var rng = RandomNumberGenerator.Create ()) - rng.GetBytes (nonce); - - var targetInfo = type2.EncodedTargetInfo; - var blob = new byte[28 + targetInfo.Length]; - blob[0] = 0x01; - blob[1] = 0x01; - - Buffer.BlockCopy (BitConverterLE.GetBytes (timestamp), 0, blob, 8, 8); - - Buffer.BlockCopy (nonce, 0, blob, 16, 8); - Buffer.BlockCopy (targetInfo, 0, blob, 28, targetInfo.Length); - - var challenge = type2.Nonce; - - var hashInput = new byte[challenge.Length + blob.Length]; - challenge.CopyTo (hashInput, 0); - blob.CopyTo (hashInput, challenge.Length); - - var blobHash = md5.ComputeHash (hashInput); - - var response = new byte[blob.Length + blobHash.Length]; - blobHash.CopyTo (response, 0); - blob.CopyTo (response, blobHash.Length); - - Array.Clear (ntlm_v2_hash, 0, ntlm_v2_hash.Length); - Array.Clear (hashInput, 0, hashInput.Length); - Array.Clear (blobHash, 0, blobHash.Length); - Array.Clear (nonce, 0, nonce.Length); - Array.Clear (blob, 0, blob.Length); - - return response; - } - } - - public static void Compute (Type2Message type2, NtlmAuthLevel level, string username, string password, string domain, out byte[] lm, out byte[] ntlm) - { - lm = null; - - switch (level) { - case NtlmAuthLevel.LM_and_NTLM: - lm = ComputeLM (password, type2.Nonce); - ntlm = ComputeNtlm (password, type2.Nonce); - break; - case NtlmAuthLevel.LM_and_NTLM_and_try_NTLMv2_Session: - if ((type2.Flags & NtlmFlags.NegotiateNtlm2Key) == 0) - goto case NtlmAuthLevel.LM_and_NTLM; - ComputeNtlmV2Session (password, type2.Nonce, out lm, out ntlm); - break; - case NtlmAuthLevel.NTLM_only: - if ((type2.Flags & NtlmFlags.NegotiateNtlm2Key) != 0) - ComputeNtlmV2Session (password, type2.Nonce, out lm, out ntlm); - else - ntlm = ComputeNtlm (password, type2.Nonce); - break; - case NtlmAuthLevel.NTLMv2_only: - ntlm = ComputeNtlmV2 (type2, username, password, domain); - break; - default: - throw new InvalidOperationException (); - } - } - - static byte[] GetResponse (byte[] challenge, byte[] pwd) - { - var response = new byte[24]; - - using (var des = DES.Create ()) { - des.Mode = CipherMode.ECB; - des.Key = PrepareDESKey (pwd, 0); - - using (var ct = des.CreateEncryptor ()) - ct.TransformBlock (challenge, 0, 8, response, 0); - - des.Key = PrepareDESKey (pwd, 7); - - using (var ct = des.CreateEncryptor ()) - ct.TransformBlock (challenge, 0, 8, response, 8); - - des.Key = PrepareDESKey (pwd, 14); - - using (var ct = des.CreateEncryptor ()) - ct.TransformBlock (challenge, 0, 8, response, 16); - } - - return response; - } - - static byte[] PrepareDESKey (byte[] key56bits, int position) - { - // convert to 8 bytes - var key = new byte[8]; - - key[0] = key56bits [position]; - key[1] = (byte) ((key56bits[position] << 7) | (key56bits[position + 1] >> 1)); - key[2] = (byte) ((key56bits[position + 1] << 6) | (key56bits[position + 2] >> 2)); - key[3] = (byte) ((key56bits[position + 2] << 5) | (key56bits[position + 3] >> 3)); - key[4] = (byte) ((key56bits[position + 3] << 4) | (key56bits[position + 4] >> 4)); - key[5] = (byte) ((key56bits[position + 4] << 3) | (key56bits[position + 5] >> 5)); - key[6] = (byte) ((key56bits[position + 5] << 2) | (key56bits[position + 6] >> 6)); - key[7] = (byte) (key56bits[position + 6] << 1); - - return key; - } - - static byte[] PasswordToKey (string password, int position) - { - int len = Math.Min (password.Length - position, 7); - var key7 = new byte[7]; - - Encoding.ASCII.GetBytes (password.ToUpper (), position, len, key7, 0); - var key8 = PrepareDESKey (key7, 0); - - // cleanup intermediate key material - Array.Clear (key7, 0, key7.Length); - - return key8; - } - } -} diff --git a/src/MailKit/Security/Ntlm/DES.cs b/src/MailKit/Security/Ntlm/DES.cs deleted file mode 100644 index 9297fbb..0000000 --- a/src/MailKit/Security/Ntlm/DES.cs +++ /dev/null @@ -1,232 +0,0 @@ -// -// DES.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Security.Cryptography; - -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Parameters; - -namespace MailKit.Security.Ntlm { - class DES : SymmetricAlgorithm - { - DES () - { - BlockSize = 64; - KeySize = 64; - } - - public static DES Create () - { - return new DES (); - } - - public override KeySizes[] LegalBlockSizes { - get { return new [] { new KeySizes (64, 64, 0) }; } - } - - public override KeySizes[] LegalKeySizes { - get { return new [] { new KeySizes (64, 64, 0) }; } - } - - public override void GenerateIV () - { - var iv = new byte[8]; - - using (var rng = RandomNumberGenerator.Create ()) - rng.GetBytes (iv); - - IV = iv; - } - - public override void GenerateKey () - { - var key = new byte[8]; - - using (var rng = RandomNumberGenerator.Create ()) { - do { - rng.GetBytes (key); - } while (IsWeakKey (key) || IsSemiWeakKey (key)); - } - - Key = key; - } - - class DesTransform : ICryptoTransform - { - readonly DesEngine engine; - - public DesTransform (bool encryption, byte[] key) - { - engine = new DesEngine (); - - engine.Init (encryption, new KeyParameter (key)); - } - - public bool CanReuseTransform { - get { return false; } - } - - public bool CanTransformMultipleBlocks { - get { return false; } - } - - public int InputBlockSize { - get { return 8; } - } - - public int OutputBlockSize { - get { return 8; } - } - - public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer == null) - throw new ArgumentNullException ("inputBuffer"); - - if (inputOffset < 0 || inputOffset > inputBuffer.Length) - throw new ArgumentOutOfRangeException ("inputOffset"); - - if (inputCount < 0 || inputOffset > inputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException ("inputCount"); - - if (inputCount != 8) - throw new ArgumentOutOfRangeException ("inputCount", "Can only transform 8 bytes at a time."); - - if (outputBuffer == null) - throw new ArgumentNullException ("outputBuffer"); - - if (outputOffset < 0 || outputOffset > outputBuffer.Length - 8) - throw new ArgumentOutOfRangeException ("outputOffset"); - - return engine.ProcessBlock (inputBuffer, inputOffset, outputBuffer, outputOffset); - } - - public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) - { - if (inputBuffer == null) - throw new ArgumentNullException ("inputBuffer"); - - if (inputOffset < 0 || inputOffset > inputBuffer.Length) - throw new ArgumentOutOfRangeException ("inputOffset"); - - if (inputCount < 0 || inputOffset > inputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException ("inputCount"); - - var output = new byte[8]; - - engine.ProcessBlock (inputBuffer, inputOffset, output, 0); - - return output; - } - - public void Dispose () - { - } - } - - public override ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV) - { - return new DesTransform (false, rgbKey); - } - - public override ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV) - { - return new DesTransform (true, rgbKey); - } - - // The following code is Copyright (C) Microsoft Corporation. All rights reserved. - - public static bool IsWeakKey (byte[] rgbKey) - { - if (!IsLegalKeySize (rgbKey)) - throw new CryptographicException ("Invalid key size."); - - byte[] rgbOddParityKey = FixupKeyParity (rgbKey); - ulong key = QuadWordFromBigEndian (rgbOddParityKey); - - return ((key == 0x0101010101010101) || - (key == 0xfefefefefefefefe) || - (key == 0x1f1f1f1f0e0e0e0e) || - (key == 0xe0e0e0e0f1f1f1f1)); - } - - public static bool IsSemiWeakKey (byte[] rgbKey) - { - if (!IsLegalKeySize (rgbKey)) - throw new CryptographicException ("Invalid key size."); - - byte[] rgbOddParityKey = FixupKeyParity (rgbKey); - ulong key = QuadWordFromBigEndian (rgbOddParityKey); - - return ((key == 0x01fe01fe01fe01fe) || - (key == 0xfe01fe01fe01fe01) || - (key == 0x1fe01fe00ef10ef1) || - (key == 0xe01fe01ff10ef10e) || - (key == 0x01e001e001f101f1) || - (key == 0xe001e001f101f101) || - (key == 0x1ffe1ffe0efe0efe) || - (key == 0xfe1ffe1ffe0efe0e) || - (key == 0x011f011f010e010e) || - (key == 0x1f011f010e010e01) || - (key == 0xe0fee0fef1fef1fe) || - (key == 0xfee0fee0fef1fef1)); - } - - static byte[] FixupKeyParity (byte[] key) - { - byte[] oddParityKey = new byte[key.Length]; - - for (int index = 0; index < key.Length; index++) { - // Get the bits we are interested in - oddParityKey[index] = (byte) (key[index] & 0xfe); - // Get the parity of the sum of the previous bits - byte tmp1 = (byte) ((oddParityKey[index] & 0xF) ^ (oddParityKey[index] >> 4)); - byte tmp2 = (byte) ((tmp1 & 0x3) ^ (tmp1 >> 2)); - byte sumBitsMod2 = (byte) ((tmp2 & 0x1) ^ (tmp2 >> 1)); - // We need to set the last bit in oddParityKey[index] to the negation - // of the last bit in sumBitsMod2 - if (sumBitsMod2 == 0) - oddParityKey[index] |= 1; - } - - return oddParityKey; - } - - static bool IsLegalKeySize (byte[] rgbKey) - { - return rgbKey != null && rgbKey.Length == 8; - } - - static ulong QuadWordFromBigEndian (byte[] block) - { - return (((ulong) block[0]) << 56) | (((ulong) block[1]) << 48) | - (((ulong) block[2]) << 40) | (((ulong) block[3]) << 32) | - (((ulong) block[4]) << 24) | (((ulong) block[5]) << 16) | - (((ulong) block[6]) << 8) | ((ulong) block[7]); - } - } -} diff --git a/src/MailKit/Security/Ntlm/HMACMD5.cs b/src/MailKit/Security/Ntlm/HMACMD5.cs deleted file mode 100644 index ea88e8e..0000000 --- a/src/MailKit/Security/Ntlm/HMACMD5.cs +++ /dev/null @@ -1,202 +0,0 @@ -// -// HMACMD5.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; - -using Org.BouncyCastle.Crypto.Macs; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Parameters; - -namespace MailKit.Security.Ntlm { - class HMACMD5 : IDisposable - { - readonly HMac hash = new HMac (new MD5Digest ()); - byte[] hashValue, key; - bool disposed; - - public HMACMD5 (byte[] key) - { - Key = key; - } - - ~HMACMD5 () - { - Dispose (false); - } - - public byte[] Hash - { - get { - if (hashValue == null) - throw new InvalidOperationException ("No hash value computed."); - - return hashValue; - } - } - - public byte[] Key { - get { return key; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (key != null) - Array.Clear (key, 0, key.Length); - - key = value; - Initialize (); - } - } - - void HashCore (byte[] block, int offset, int size) - { - hash.BlockUpdate (block, offset, size); - } - - byte[] HashFinal () - { - var value = new byte[hash.GetMacSize ()]; - - hash.DoFinal (value, 0); - hash.Reset (); - - return value; - } - - public void Initialize () - { - hash.Init (new KeyParameter (Key)); - } - - public void Clear () - { - Dispose (false); - } - - public byte[] ComputeHash (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || offset > buffer.Length - count) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (disposed) - throw new ObjectDisposedException ("HashAlgorithm"); - - HashCore (buffer, offset, count); - hashValue = HashFinal (); - - return hashValue; - } - - public byte[] ComputeHash (byte[] buffer) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - return ComputeHash (buffer, 0, buffer.Length); - } - - public byte[] ComputeHash (Stream inputStream) - { - // don't read stream unless object is ready to use - if (disposed) - throw new ObjectDisposedException ("HashAlgorithm"); - - var buffer = new byte[4096]; - int nread; - - do { - if ((nread = inputStream.Read (buffer, 0, buffer.Length)) > 0) - HashCore (buffer, 0, nread); - } while (nread > 0); - - hashValue = HashFinal (); - - return hashValue; - } - - public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer == null) - throw new ArgumentNullException (nameof (inputBuffer)); - - if (inputOffset < 0 || inputOffset > inputBuffer.Length) - throw new ArgumentOutOfRangeException (nameof (inputOffset)); - - if (inputCount < 0 || inputOffset > inputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException (nameof (inputCount)); - - if (outputBuffer != null) { - if (outputOffset < 0 || outputOffset > outputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException (nameof (outputOffset)); - } - - HashCore (inputBuffer, inputOffset, inputCount); - - if (outputBuffer != null) - Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); - - return inputCount; - } - - public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) - { - if (inputCount < 0) - throw new ArgumentOutOfRangeException (nameof (inputCount)); - - var outputBuffer = new byte[inputCount]; - - // note: other exceptions are handled by Buffer.BlockCopy - Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, 0, inputCount); - - HashCore (inputBuffer, inputOffset, inputCount); - hashValue = HashFinal (); - - return outputBuffer; - } - - void Dispose (bool disposing) - { - if (key != null) { - Array.Clear (key, 0, Key.Length); - key = null; - } - } - - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - disposed = true; - } - } -} diff --git a/src/MailKit/Security/Ntlm/MD4.cs b/src/MailKit/Security/Ntlm/MD4.cs deleted file mode 100644 index 2f8ab7b..0000000 --- a/src/MailKit/Security/Ntlm/MD4.cs +++ /dev/null @@ -1,410 +0,0 @@ -// -// MD4.cs -// -// Authors: Sebastien Pouliot -// Jeffrey Stedfast -// -// Copyright (c) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (c) 2004-2005, 2010 Novell, Inc (http://www.novell.com) -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.IO; - -namespace MailKit.Security.Ntlm { - sealed class MD4 : IDisposable - { - const int S11 = 3; - const int S12 = 7; - const int S13 = 11; - const int S14 = 19; - const int S21 = 3; - const int S22 = 5; - const int S23 = 9; - const int S24 = 13; - const int S31 = 3; - const int S32 = 9; - const int S33 = 11; - const int S34 = 15; - - bool disposed; - byte[] hashValue; - byte[] buffered; - uint[] state; - uint[] count; - uint[] x; - - public MD4 () - { - // we allocate the context memory - buffered = new byte[64]; - state = new uint[4]; - count = new uint[2]; - - // temporary buffer in MD4Transform that we don't want to allocate on each iteration - x = new uint[16]; - - // the initialize our context - Initialize (); - } - - ~MD4 () - { - Dispose (false); - } - - public byte[] Hash { - get { - if (hashValue == null) - throw new InvalidOperationException ("No hash value computed."); - - return hashValue; - } - } - - void HashCore (byte[] block, int offset, int size) - { - // Compute number of bytes mod 64 - int index = (int) ((count[0] >> 3) & 0x3F); - - // Update number of bits - count[0] += (uint) (size << 3); - if (count[0] < (size << 3)) - count[1]++; - - count[1] += (uint) (size >> 29); - - int partLen = 64 - index; - int i = 0; - - // Transform as many times as possible. - if (size >= partLen) { - Buffer.BlockCopy (block, offset, buffered, index, partLen); - MD4Transform (buffered, 0); - - for (i = partLen; i + 63 < size; i += 64) - MD4Transform (block, offset + i); - - index = 0; - } - - // Buffer remaining input - Buffer.BlockCopy (block, offset + i, buffered, index, size - i); - } - - byte[] HashFinal () - { - // Save number of bits - var bits = new byte[8]; - Encode (bits, count); - - // Pad out to 56 mod 64. - uint index = ((count [0] >> 3) & 0x3f); - int padLen = (int) ((index < 56) ? (56 - index) : (120 - index)); - HashCore (Padding (padLen), 0, padLen); - - // Append length (before padding) - HashCore (bits, 0, 8); - - // Store state in digest - var digest = new byte[16]; - Encode (digest, state); - - return digest; - } - - public void Initialize () - { - count[0] = 0; - count[1] = 0; - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; - - // Clear sensitive information - Array.Clear (buffered, 0, 64); - Array.Clear (x, 0, 16); - } - - static byte[] Padding (int length) - { - if (length > 0) { - var padding = new byte[length]; - padding[0] = 0x80; - return padding; - } - - return null; - } - - // F, G and H are basic MD4 functions. - static uint F (uint x, uint y, uint z) - { - return (x & y) | (~x & z); - } - - static uint G (uint x, uint y, uint z) - { - return (x & y) | (x & z) | (y & z); - } - - static uint H (uint x, uint y, uint z) - { - return x ^ y ^ z; - } - - // ROTATE_LEFT rotates x left n bits. - static uint ROL (uint x, byte n) - { - return (x << n) | (x >> (32 - n)); - } - - /* FF, GG and HH are transformations for rounds 1, 2 and 3 */ - /* Rotation is separate from addition to prevent recomputation */ - static void FF (ref uint a, uint b, uint c, uint d, uint x, byte s) - { - a += F (b, c, d) + x; - a = ROL (a, s); - } - - static void GG (ref uint a, uint b, uint c, uint d, uint x, byte s) - { - a += G (b, c, d) + x + 0x5a827999; - a = ROL (a, s); - } - - static void HH (ref uint a, uint b, uint c, uint d, uint x, byte s) - { - a += H (b, c, d) + x + 0x6ed9eba1; - a = ROL (a, s); - } - - static void Encode (byte[] output, uint[] input) - { - for (int i = 0, j = 0; j < output.Length; i++, j += 4) { - output[j + 0] = (byte) (input[i]); - output[j + 1] = (byte) (input[i] >> 8); - output[j + 2] = (byte) (input[i] >> 16); - output[j + 3] = (byte) (input[i] >> 24); - } - } - - static void Decode (uint[] output, byte[] input, int index) - { - for (int i = 0, j = index; i < output.Length; i++, j += 4) - output[i] = (uint) ((input[j]) | (input[j + 1] << 8) | (input[j + 2] << 16) | (input[j + 3] << 24)); - } - - void MD4Transform (byte[] block, int index) - { - uint a = state[0]; - uint b = state[1]; - uint c = state[2]; - uint d = state[3]; - - Decode (x, block, index); - - /* Round 1 */ - FF (ref a, b, c, d, x[ 0], S11); /* 1 */ - FF (ref d, a, b, c, x[ 1], S12); /* 2 */ - FF (ref c, d, a, b, x[ 2], S13); /* 3 */ - FF (ref b, c, d, a, x[ 3], S14); /* 4 */ - FF (ref a, b, c, d, x[ 4], S11); /* 5 */ - FF (ref d, a, b, c, x[ 5], S12); /* 6 */ - FF (ref c, d, a, b, x[ 6], S13); /* 7 */ - FF (ref b, c, d, a, x[ 7], S14); /* 8 */ - FF (ref a, b, c, d, x[ 8], S11); /* 9 */ - FF (ref d, a, b, c, x[ 9], S12); /* 10 */ - FF (ref c, d, a, b, x[10], S13); /* 11 */ - FF (ref b, c, d, a, x[11], S14); /* 12 */ - FF (ref a, b, c, d, x[12], S11); /* 13 */ - FF (ref d, a, b, c, x[13], S12); /* 14 */ - FF (ref c, d, a, b, x[14], S13); /* 15 */ - FF (ref b, c, d, a, x[15], S14); /* 16 */ - - /* Round 2 */ - GG (ref a, b, c, d, x[ 0], S21); /* 17 */ - GG (ref d, a, b, c, x[ 4], S22); /* 18 */ - GG (ref c, d, a, b, x[ 8], S23); /* 19 */ - GG (ref b, c, d, a, x[12], S24); /* 20 */ - GG (ref a, b, c, d, x[ 1], S21); /* 21 */ - GG (ref d, a, b, c, x[ 5], S22); /* 22 */ - GG (ref c, d, a, b, x[ 9], S23); /* 23 */ - GG (ref b, c, d, a, x[13], S24); /* 24 */ - GG (ref a, b, c, d, x[ 2], S21); /* 25 */ - GG (ref d, a, b, c, x[ 6], S22); /* 26 */ - GG (ref c, d, a, b, x[10], S23); /* 27 */ - GG (ref b, c, d, a, x[14], S24); /* 28 */ - GG (ref a, b, c, d, x[ 3], S21); /* 29 */ - GG (ref d, a, b, c, x[ 7], S22); /* 30 */ - GG (ref c, d, a, b, x[11], S23); /* 31 */ - GG (ref b, c, d, a, x[15], S24); /* 32 */ - - HH (ref a, b, c, d, x[ 0], S31); /* 33 */ - HH (ref d, a, b, c, x[ 8], S32); /* 34 */ - HH (ref c, d, a, b, x[ 4], S33); /* 35 */ - HH (ref b, c, d, a, x[12], S34); /* 36 */ - HH (ref a, b, c, d, x[ 2], S31); /* 37 */ - HH (ref d, a, b, c, x[10], S32); /* 38 */ - HH (ref c, d, a, b, x[ 6], S33); /* 39 */ - HH (ref b, c, d, a, x[14], S34); /* 40 */ - HH (ref a, b, c, d, x[ 1], S31); /* 41 */ - HH (ref d, a, b, c, x[ 9], S32); /* 42 */ - HH (ref c, d, a, b, x[ 5], S33); /* 43 */ - HH (ref b, c, d, a, x[13], S34); /* 44 */ - HH (ref a, b, c, d, x[ 3], S31); /* 45 */ - HH (ref d, a, b, c, x[11], S32); /* 46 */ - HH (ref c, d, a, b, x[ 7], S33); /* 47 */ - HH (ref b, c, d, a, x[15], S34); /* 48 */ - - state [0] += a; - state [1] += b; - state [2] += c; - state [3] += d; - } - - public byte[] ComputeHash (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || offset > buffer.Length - count) - throw new ArgumentOutOfRangeException (nameof (count)); - - if (disposed) - throw new ObjectDisposedException (nameof (MD4)); - - HashCore (buffer, offset, count); - hashValue = HashFinal (); - Initialize (); - - return hashValue; - } - - public byte[] ComputeHash (byte[] buffer) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - return ComputeHash (buffer, 0, buffer.Length); - } - - public byte[] ComputeHash (Stream inputStream) - { - if (inputStream == null) - throw new ArgumentNullException (nameof (inputStream)); - - // don't read stream unless object is ready to use - if (disposed) - throw new ObjectDisposedException (nameof (MD4)); - - var buffer = new byte[4096]; - int nread; - - do { - if ((nread = inputStream.Read (buffer, 0, buffer.Length)) > 0) - HashCore (buffer, 0, nread); - } while (nread > 0); - - hashValue = HashFinal (); - Initialize (); - - return hashValue; - } - - public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer == null) - throw new ArgumentNullException (nameof (inputBuffer)); - - if (inputOffset < 0 || inputOffset > inputBuffer.Length) - throw new ArgumentOutOfRangeException (nameof (inputOffset)); - - if (inputCount < 0 || inputOffset > inputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException (nameof (inputCount)); - - if (outputBuffer != null) { - if (outputOffset < 0 || outputOffset > outputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException (nameof (outputOffset)); - } - - HashCore (inputBuffer, inputOffset, inputCount); - - if (outputBuffer != null) - Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); - - return inputCount; - } - - public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) - { - if (inputCount < 0) - throw new ArgumentOutOfRangeException (nameof (inputCount)); - - var outputBuffer = new byte[inputCount]; - - // note: other exceptions are handled by Buffer.BlockCopy - Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, 0, inputCount); - - HashCore (inputBuffer, inputOffset, inputCount); - hashValue = HashFinal (); - Initialize (); - - return outputBuffer; - } - - void Dispose (bool 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; - } - } - - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - disposed = true; - } - } -} diff --git a/src/MailKit/Security/Ntlm/MessageBase.cs b/src/MailKit/Security/Ntlm/MessageBase.cs deleted file mode 100644 index 5cc624d..0000000 --- a/src/MailKit/Security/Ntlm/MessageBase.cs +++ /dev/null @@ -1,99 +0,0 @@ -// -// Mono.Security.Protocol.Ntlm.MessageBase -// abstract class for all NTLM messages -// -// Author: -// Sebastien Pouliot -// -// Copyright (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (C) 2004 Novell, Inc (http://www.novell.com) -// -// References -// a. NTLM Authentication Scheme for HTTP, Ronald Tschalär -// http://www.innovation.ch/java/ntlm.html -// b. The NTLM Authentication Protocol, Copyright © 2003 Eric Glass -// http://davenport.sourceforge.net/ntlm.html -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Globalization; - -namespace MailKit.Security.Ntlm { - abstract class MessageBase - { - static readonly byte[] header = { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00 }; - - protected MessageBase (int type) - { - Type = type; - } - - public NtlmFlags Flags { - get; set; - } - - public int Type { - get; private set; - } - - protected byte[] PrepareMessage (int size) - { - var message = new byte[size]; - - Buffer.BlockCopy (header, 0, message, 0, 8); - - message[ 8] = (byte) Type; - message[ 9] = (byte)(Type >> 8); - message[10] = (byte)(Type >> 16); - message[11] = (byte)(Type >> 24); - - return message; - } - - bool CheckHeader (byte[] message, int startIndex) - { - for (int i = 0; i < header.Length; i++) { - if (message[startIndex + i] != header[i]) - return false; - } - - return BitConverterLE.ToUInt32 (message, startIndex + 8) == Type; - } - - protected void ValidateArguments (byte[] message, int startIndex, int length) - { - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (startIndex < 0 || startIndex > message.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 12 || length > (message.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (!CheckHeader (message, startIndex)) - throw new ArgumentException (string.Format (CultureInfo.InvariantCulture, "Invalid Type{0} message.", Type), nameof (message)); - } - - public abstract byte[] Encode (); - } -} diff --git a/src/MailKit/Security/Ntlm/NtlmAuthLevel.cs b/src/MailKit/Security/Ntlm/NtlmAuthLevel.cs deleted file mode 100644 index 584d164..0000000 --- a/src/MailKit/Security/Ntlm/NtlmAuthLevel.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// NtlmAuthLevel.cs -// -// Author: -// Martin Baulig -// -// Copyright (c) 2012 Xamarin Inc. (http://www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -namespace MailKit.Security.Ntlm { - /* - * On Windows, this is controlled by a registry setting - * (http://msdn.microsoft.com/en-us/library/ms814176.aspx) - * - * This can be configured by setting the static - * Type3Message.DefaultAuthLevel property, the default value - * is LM_and_NTLM_and_try_NTLMv2_Session. - */ - enum NtlmAuthLevel { - /* Use LM and NTLM, never use NTLMv2 session security. */ - LM_and_NTLM, - - /* Use NTLMv2 session security if the server supports it, - * otherwise fall back to LM and NTLM. */ - LM_and_NTLM_and_try_NTLMv2_Session, - - /* Use NTLMv2 session security if the server supports it, - * otherwise fall back to NTLM. Never use LM. */ - NTLM_only, - - /* Use NTLMv2 only. */ - NTLMv2_only, - } -} diff --git a/src/MailKit/Security/Ntlm/NtlmFlags.cs b/src/MailKit/Security/Ntlm/NtlmFlags.cs deleted file mode 100644 index 2d96a33..0000000 --- a/src/MailKit/Security/Ntlm/NtlmFlags.cs +++ /dev/null @@ -1,222 +0,0 @@ -// -// NtlmFlags.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit.Security.Ntlm { - /// - /// The NTLM message header flags. - /// - /// - /// More details here: http://davenport.sourceforge.net/ntlm.html#theNtlmMessageHeaderLayout - /// and at https://msdn.microsoft.com/en-us/library/cc236650.aspx - /// - [Flags] - enum NtlmFlags { - /// - /// Indicates that Unicode strings are supported for use in security buffer data. - /// - NegotiateUnicode = 0x00000001, - - /// - /// Indicates that OEM strings are supported for use in security buffer data. - /// - NegotiateOem = 0x00000002, - - /// - /// Requests that the server's authentication realm be included in the Type 2 message. - /// - RequestTarget = 0x00000004, - - /// - /// This flag's usage has not been identified. - /// - R10 = 0x00000008, - - /// - /// Specifies that authenticated communication between the client and server should carry a digital signature (message integrity). - /// - NegotiateSign = 0x00000010, - - /// - /// Specifies that authenticated communication between the client and server should be encrypted (message confidentiality). - /// - NegotiateSeal = 0x00000020, - - /// - /// Indicates that datagram authentication is being used. - /// - NegotiateDatagramStyle = 0x00000040, - - /// - /// Indicates that the Lan Manager Session Key should be used for signing - /// and sealing authenticated communications. - /// - NegotiateLanManagerKey = 0x00000080, - - /// - /// This flag is unused and MUST be zero. (r8) - /// - R9 = 0x00000100, - - /// - /// Indicates that NTLM authentication is being used. - /// - NegotiateNtlm = 0x00000200, - - /// - /// This flag is unused and MUST be zero. (r8) - /// - R8 = 0x00000400, - - /// - /// Sent by the client in the Type 3 message to indicate that an anonymous - /// context has been established. This also affects the response fields. - /// - NegotiateAnonymous = 0x00000800, - - /// - /// Sent by the client in the Type 1 message to indicate that the name of the - /// domain in which the client workstation has membership is included in the - /// message. This is used by the server to determine whether the client is - /// eligible for local authentication. - /// - NegotiateDomainSupplied = 0x00001000, - - /// - /// Sent by the client in the Type 1 message to indicate that the client - /// workstation's name is included in the message. This is used by the server - /// to determine whether the client is eligible for local authentication. - /// - NegotiateWorkstationSupplied = 0x00002000, - - /// - /// Sent by the server to indicate that the server and client are on the same - /// machine. Implies that the client may use the established local credentials - /// for authentication instead of calculating a response to the challenge. - /// - NegotiateLocalCall = 0x00004000, - R7 = NegotiateLocalCall, - - /// - /// Indicates that authenticated communication between the client and server - /// should be signed with a "dummy" signature. - /// - NegotiateAlwaysSign = 0x00008000, - - /// - /// Sent by the server in the Type 2 message to indicate that the target - /// authentication realm is a domain. - /// - TargetTypeDomain = 0x00010000, - - /// - /// Sent by the server in the Type 2 message to indicate that the target - /// authentication realm is a server. - /// - TargetTypeServer = 0x00020000, - - /// - /// Sent by the server in the Type 2 message to indicate that the target - /// authentication realm is a share. Presumably, this is for share-level - /// authentication. Usage is unclear. - /// - TargetTypeShare = 0x00040000, - R6 = TargetTypeShare, - - /// - /// Indicates that the NTLM2 signing and sealing scheme should be used for - /// protecting authenticated communications. Note that this refers to a - /// particular session security scheme, and is not related to the use of - /// NTLMv2 authentication. This flag can, however, have an effect on the - /// response calculations. - /// - NegotiateNtlm2Key = 0x00080000, - - /// - /// This flag's usage has not been identified. - /// - NegotiateIdentify = 0x00100000, - - /// - /// This flag is unused and MUST be zero. (r5) - /// - R5 = 0x00200000, - - /// - /// Indicates that the LMOWF function should be used to generate a session key. - /// - RequestNonNTSessionKey = 0x00400000, - - /// - /// Sent by the server in the Type 2 message to indicate that it is including - /// a Target Information block in the message. The Target Information block - /// is used in the calculation of the NTLMv2 response. - /// - NegotiateTargetInfo = 0x00800000, - - /// - /// This flag is unused and MUST be zero. (r4) - /// - R4 = 0x01000000, - - /// - /// Indicates that the version field is present. - /// - NegotiateVersion = 0x02000000, - - /// - /// This flag is unused and MUST be zero. (r3) - /// - R3 = 0x04000000, - - /// - /// This flag is unused and MUST be zero. (r2) - /// - R2 = 0x08000000, - - /// - /// This flag is unused and MUST be zero. (r1) - /// - R1 = 0x10000000, - - /// - /// Indicates that 128-bit encryption is supported. - /// - Negotiate128 = 0x20000000, - - /// - /// Indicates that the client will provide an encrypted master key in the - /// "Session Key" field of the Type 3 message. - /// - NegotiateKeyExchange = 0x40000000, - - /// - /// Indicates that 56-bit encryption is supported. - /// - Negotiate56 = (unchecked ((int) 0x80000000)) - } -} diff --git a/src/MailKit/Security/Ntlm/TargetInfo.cs b/src/MailKit/Security/Ntlm/TargetInfo.cs deleted file mode 100644 index 946ec77..0000000 --- a/src/MailKit/Security/Ntlm/TargetInfo.cs +++ /dev/null @@ -1,261 +0,0 @@ -// -// TargetInfo.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -namespace MailKit.Security.Ntlm { - class TargetInfo - { - public TargetInfo (byte[] buffer, int startIndex, int length, bool unicode) - { - Decode (buffer, startIndex, length, unicode); - } - - public TargetInfo () - { - } - - public int? Flags { - get; set; - } - - public string DomainName { - get; set; - } - - public string ServerName { - get; set; - } - - public string DnsDomainName { - get; set; - } - - public string DnsServerName { - get; set; - } - - public string DnsTreeName { - get; set; - } - - public string TargetName { - get; set; - } - - public long Timestamp { - get; set; - } - - static string DecodeString (byte[] buffer, ref int index, bool unicode) - { - var encoding = unicode ? Encoding.Unicode : Encoding.UTF8; - var length = BitConverterLE.ToInt16 (buffer, index); - var value = encoding.GetString (buffer, index + 2, length); - - index += 2 + length; - - return value; - } - - static int DecodeFlags (byte[] buffer, ref int index) - { - short nbytes = BitConverterLE.ToInt16 (buffer, index); - int flags; - - index += 2; - - switch (nbytes) { - case 4: flags = BitConverterLE.ToInt32 (buffer, index); break; - case 2: flags = BitConverterLE.ToInt16 (buffer, index); break; - default: flags = 0; break; - } - - index += nbytes; - - return flags; - } - - static long DecodeTimestamp (byte[] buffer, ref int index) - { - short nbytes = BitConverterLE.ToInt16 (buffer, index); - long lo, hi; - - index += 2; - - switch (nbytes) { - case 8: - lo = BitConverterLE.ToUInt32 (buffer, index); - index += 4; - hi = BitConverterLE.ToUInt32 (buffer, index); - index += 4; - return (hi << 32) | lo; - case 4: - lo = BitConverterLE.ToUInt32 (buffer, index); - index += 4; - return lo; - case 2: - lo = BitConverterLE.ToUInt16 (buffer, index); - index += 2; - return lo; - default: - index += nbytes; - return 0; - } - } - - void Decode (byte[] buffer, int startIndex, int length, bool unicode) - { - int index = startIndex; - - do { - var type = BitConverterLE.ToInt16 (buffer, index); - - index += 2; - - switch (type) { - case 0: index = startIndex + length; break; // a 'type' of 0 terminates the TargetInfo - case 1: ServerName = DecodeString (buffer, ref index, unicode); break; - case 2: DomainName = DecodeString (buffer, ref index, unicode); break; - case 3: DnsServerName = DecodeString (buffer, ref index, unicode); break; - case 4: DnsDomainName = DecodeString (buffer, ref index, unicode); break; - case 5: DnsTreeName = DecodeString (buffer, ref index, unicode); break; - case 6: Flags = DecodeFlags (buffer, ref index); break; - case 7: Timestamp = DecodeTimestamp (buffer, ref index); break; - case 9: TargetName = DecodeString (buffer, ref index, unicode); break; - default: index += 2 + BitConverterLE.ToInt16 (buffer, index); break; - } - } while (index < startIndex + length); - } - - int CalculateSize (bool unicode) - { - var encoding = unicode ? Encoding.Unicode : Encoding.UTF8; - int length = 4; - - if (!string.IsNullOrEmpty (DomainName)) - length += 4 + encoding.GetByteCount (DomainName); - - if (!string.IsNullOrEmpty (ServerName)) - length += 4 + encoding.GetByteCount (ServerName); - - if (!string.IsNullOrEmpty (DnsDomainName)) - length += 4 + encoding.GetByteCount (DnsDomainName); - - if (!string.IsNullOrEmpty (DnsServerName)) - length += 4 + encoding.GetByteCount (DnsServerName); - - if (!string.IsNullOrEmpty (DnsTreeName)) - length += 4 + encoding.GetByteCount (DnsTreeName); - - if (Flags.HasValue) - length += 8; - - if (Timestamp != 0) - length += 12; - - if (!string.IsNullOrEmpty (TargetName)) - length += 4 + encoding.GetByteCount (TargetName); - - return length; - } - - static void EncodeTypeAndLength (byte[] buf, ref int index, short type, short length) - { - buf[index++] = (byte) (type); - buf[index++] = (byte) (type >> 8); - buf[index++] = (byte) (length); - buf[index++] = (byte) (length >> 8); - } - - static void EncodeString (byte[] buf, ref int index, short type, string value, bool unicode) - { - var encoding = unicode ? Encoding.Unicode : Encoding.UTF8; - int length = value.Length; - - if (unicode) - length *= 2; - - EncodeTypeAndLength (buf, ref index, type, (short) length); - encoding.GetBytes (value, 0, value.Length, buf, index); - index += length; - } - - static void EncodeInt32 (byte[] buf, ref int index, int value) - { - buf[index++] = (byte) (value); - buf[index++] = (byte) (value >> 8); - buf[index++] = (byte) (value >> 16); - buf[index++] = (byte) (value >> 24); - } - - static void EncodeTimestamp (byte[] buf, ref int index, short type, long value) - { - EncodeTypeAndLength (buf, ref index, type, 8); - EncodeInt32 (buf, ref index, (int) (value & 0xffffffff)); - EncodeInt32 (buf, ref index, (int) (value >> 32)); - } - - static void EncodeFlags (byte[] buf, ref int index, short type, int value) - { - EncodeTypeAndLength (buf, ref index, type, 4); - EncodeInt32 (buf, ref index, value); - } - - public byte[] Encode (bool unicode) - { - var buf = new byte[CalculateSize (unicode)]; - int index = 0; - - if (!string.IsNullOrEmpty (DomainName)) - EncodeString (buf, ref index, 2, DomainName, unicode); - - if (!string.IsNullOrEmpty (ServerName)) - EncodeString (buf, ref index, 1, ServerName, unicode); - - if (!string.IsNullOrEmpty (DnsDomainName)) - EncodeString (buf, ref index, 4, DnsDomainName, unicode); - - if (!string.IsNullOrEmpty (DnsServerName)) - EncodeString (buf, ref index, 3, DnsServerName, unicode); - - if (!string.IsNullOrEmpty (DnsTreeName)) - EncodeString (buf, ref index, 5, DnsTreeName, unicode); - - if (Flags.HasValue) - EncodeFlags (buf, ref index, 6, Flags.Value); - - if (Timestamp != 0) - EncodeTimestamp (buf, ref index, 7, Timestamp); - - if (!string.IsNullOrEmpty (TargetName)) - EncodeString (buf, ref index, 9, TargetName, unicode); - - return buf; - } - } -} diff --git a/src/MailKit/Security/Ntlm/Type1Message.cs b/src/MailKit/Security/Ntlm/Type1Message.cs deleted file mode 100644 index 1f15dc7..0000000 --- a/src/MailKit/Security/Ntlm/Type1Message.cs +++ /dev/null @@ -1,167 +0,0 @@ -// -// Mono.Security.Protocol.Ntlm.Type1Message - Negotiation -// -// Authors: Sebastien Pouliot -// Jeffrey Stedfast -// -// Copyright (c) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (c) 2004 Novell, Inc (http://www.novell.com) -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// References -// a. NTLM Authentication Scheme for HTTP, Ronald Tschalär -// http://www.innovation.ch/java/ntlm.html -// b. The NTLM Authentication Protocol, Copyright © 2003 Eric Glass -// http://davenport.sourceforge.net/ntlm.html -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Text; - -namespace MailKit.Security.Ntlm { - class Type1Message : MessageBase - { - internal static readonly NtlmFlags DefaultFlags = NtlmFlags.NegotiateNtlm | NtlmFlags.NegotiateOem | NtlmFlags.NegotiateUnicode | NtlmFlags.RequestTarget; - - string domain; - string host; - - public Type1Message (string hostName, string domainName) : base (1) - { - Flags = DefaultFlags; - Domain = domainName; - Host = hostName; - } - - public Type1Message (byte[] message, int startIndex, int length) : base (1) - { - Decode (message, startIndex, length); - } - - public string Domain { - get { return domain; } - set { - if (string.IsNullOrEmpty (value)) { - Flags &= ~NtlmFlags.NegotiateDomainSupplied; - value = string.Empty; - } else { - Flags |= NtlmFlags.NegotiateDomainSupplied; - } - - domain = value; - } - } - - public string Host { - get { return host; } - set { - if (string.IsNullOrEmpty (value)) { - Flags &= ~NtlmFlags.NegotiateWorkstationSupplied; - value = string.Empty; - } else { - Flags |= NtlmFlags.NegotiateWorkstationSupplied; - } - - host = value; - } - } - - public Version OSVersion { - get; set; - } - - void Decode (byte[] message, int startIndex, int length) - { - int offset, count; - - ValidateArguments (message, startIndex, length); - - Flags = (NtlmFlags) BitConverterLE.ToUInt32 (message, startIndex + 12); - - // decode the domain - count = BitConverterLE.ToUInt16 (message, startIndex + 16); - offset = BitConverterLE.ToUInt16 (message, startIndex + 20); - domain = Encoding.UTF8.GetString (message, startIndex + offset, count); - - // decode the workstation/host - count = BitConverterLE.ToUInt16 (message, startIndex + 24); - offset = BitConverterLE.ToUInt16 (message, startIndex + 28); - host = Encoding.UTF8.GetString (message, startIndex + offset, count); - - if (offset == 40) { - // decode the OS Version - int major = message[startIndex + 32]; - int minor = message[startIndex + 33]; - int build = BitConverterLE.ToUInt16 (message, startIndex + 34); - - OSVersion = new Version (major, minor, build); - } - } - - public override byte[] Encode () - { - int versionLength = OSVersion != null ? 8 : 0; - int hostOffset = 32 + versionLength; - int domainOffset = hostOffset + host.Length; - - var message = PrepareMessage (32 + domain.Length + host.Length + versionLength); - - message[12] = (byte) Flags; - message[13] = (byte)((uint) Flags >> 8); - message[14] = (byte)((uint) Flags >> 16); - message[15] = (byte)((uint) Flags >> 24); - - message[16] = (byte) domain.Length; - message[17] = (byte)(domain.Length >> 8); - message[18] = message[16]; - message[19] = message[17]; - message[20] = (byte) domainOffset; - message[21] = (byte)(domainOffset >> 8); - - message[24] = (byte) host.Length; - message[25] = (byte)(host.Length >> 8); - message[26] = message[24]; - message[27] = message[25]; - message[28] = (byte) hostOffset; - message[29] = (byte)(hostOffset >> 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; - message[39] = 0x0f; - } - - var hostName = Encoding.UTF8.GetBytes (host.ToUpperInvariant ()); - Buffer.BlockCopy (hostName, 0, message, hostOffset, hostName.Length); - - var domainName = Encoding.UTF8.GetBytes (domain.ToUpperInvariant ()); - Buffer.BlockCopy (domainName, 0, message, domainOffset, domainName.Length); - - return message; - } - } -} diff --git a/src/MailKit/Security/Ntlm/Type2Message.cs b/src/MailKit/Security/Ntlm/Type2Message.cs deleted file mode 100644 index d00c254..0000000 --- a/src/MailKit/Security/Ntlm/Type2Message.cs +++ /dev/null @@ -1,191 +0,0 @@ -// -// Mono.Security.Protocol.Ntlm.Type2Message - Challenge -// -// Authors: Sebastien Pouliot -// Jeffrey Stedfast -// -// Copyright (c) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (c) 2004 Novell, Inc (http://www.novell.com) -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// References -// a. NTLM Authentication Scheme for HTTP, Ronald Tschalär -// http://www.innovation.ch/java/ntlm.html -// b. The NTLM Authentication Protocol, Copyright © 2003 Eric Glass -// http://davenport.sourceforge.net/ntlm.html -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Text; -using System.Security.Cryptography; - -namespace MailKit.Security.Ntlm { - class Type2Message : MessageBase - { - byte[] targetInfo; - byte[] nonce; - - public Type2Message () : base (2) - { - Flags = NtlmFlags.NegotiateNtlm | NtlmFlags.NegotiateUnicode /*| NtlmFlags.NegotiateAlwaysSign*/; - nonce = new byte[8]; - - using (var rng = RandomNumberGenerator.Create ()) - rng.GetBytes (nonce); - } - - public Type2Message (byte[] message, int startIndex, int length) : base (2) - { - nonce = new byte[8]; - Decode (message, startIndex, length); - } - - ~Type2Message () - { - if (nonce != null) - Array.Clear (nonce, 0, nonce.Length); - } - - public byte[] Nonce { - get { return (byte[]) nonce.Clone (); } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (value.Length != 8) - throw new ArgumentException ("Invalid Nonce Length (should be 8 bytes).", nameof (value)); - - nonce = (byte[]) value.Clone (); - } - } - - public string TargetName { - get; set; - } - - public TargetInfo TargetInfo { - get; set; - } - - public byte[] EncodedTargetInfo { - get { - if (targetInfo != null) - return (byte[]) targetInfo.Clone (); - - return new byte[0]; - } - } - - void Decode (byte[] message, int startIndex, int length) - { - ValidateArguments (message, startIndex, length); - - Flags = (NtlmFlags) BitConverterLE.ToUInt32 (message, startIndex + 20); - - Buffer.BlockCopy (message, startIndex + 24, nonce, 0, 8); - - var targetNameLength = BitConverterLE.ToUInt16 (message, startIndex + 12); - var targetNameOffset = BitConverterLE.ToUInt16 (message, startIndex + 16); - - if (targetNameLength > 0) { - var encoding = (Flags & NtlmFlags.NegotiateUnicode) != 0 ? Encoding.Unicode : Encoding.UTF8; - - TargetName = encoding.GetString (message, startIndex + targetNameOffset, targetNameLength); - } - - // The Target Info block is optional. - if (length >= 48 && targetNameOffset >= 48) { - var targetInfoLength = BitConverterLE.ToUInt16 (message, startIndex + 40); - var targetInfoOffset = BitConverterLE.ToUInt16 (message, startIndex + 44); - - if (targetInfoLength > 0 && targetInfoOffset < length && targetInfoLength <= (length - targetInfoOffset)) { - TargetInfo = new TargetInfo (message, startIndex + targetInfoOffset, targetInfoLength, (Flags & NtlmFlags.NegotiateOem) == 0); - - targetInfo = new byte[targetInfoLength]; - Buffer.BlockCopy (message, startIndex + targetInfoOffset, targetInfo, 0, targetInfoLength); - } - } - } - - public override byte[] Encode () - { - int targetNameOffset = 40; - int targetInfoOffset = 48; - byte[] targetName = null; - int size = 40; - - if (TargetName != null) { - var encoding = (Flags & NtlmFlags.NegotiateUnicode) != 0 ? Encoding.Unicode : Encoding.UTF8; - - targetName = encoding.GetBytes (TargetName); - targetInfoOffset += targetName.Length; - size += targetName.Length; - } - - if (TargetInfo != null || targetInfo != null) { - if (targetInfo == null) - targetInfo = TargetInfo.Encode ((Flags & NtlmFlags.NegotiateUnicode) != 0); - size += targetInfo.Length + 8; - targetNameOffset += 8; - } - - var data = PrepareMessage (size); - - // message length - short length = (short) data.Length; - data[16] = (byte) length; - data[17] = (byte)(length >> 8); - - // flags - data[20] = (byte) Flags; - data[21] = (byte)((uint) Flags >> 8); - data[22] = (byte)((uint) Flags >> 16); - data[23] = (byte)((uint) Flags >> 24); - - Buffer.BlockCopy (nonce, 0, data, 24, nonce.Length); - - if (targetName != null) { - data[12] = (byte) targetName.Length; - data[13] = (byte)(targetName.Length >> 8); - data[14] = (byte)targetName.Length; - data[15] = (byte)(targetName.Length >> 8); - data[16] = (byte) targetNameOffset; - data[17] = (byte)(targetNameOffset >> 8); - - Buffer.BlockCopy (targetName, 0, data, targetNameOffset, targetName.Length); - } - - if (targetInfo != null) { - data[40] = (byte) targetInfo.Length; - data[41] = (byte)(targetInfo.Length >> 8); - data[42] = (byte) targetInfo.Length; - data[43] = (byte)(targetInfo.Length >> 8); - data[44] = (byte) targetInfoOffset; - data[45] = (byte)(targetInfoOffset >> 8); - - Buffer.BlockCopy (targetInfo, 0, data, targetInfoOffset, targetInfo.Length); - } - - return data; - } - } -} diff --git a/src/MailKit/Security/Ntlm/Type3Message.cs b/src/MailKit/Security/Ntlm/Type3Message.cs deleted file mode 100644 index 79e11b9..0000000 --- a/src/MailKit/Security/Ntlm/Type3Message.cs +++ /dev/null @@ -1,284 +0,0 @@ -// -// Mono.Security.Protocol.Ntlm.Type3Message - Authentication -// -// Authors: Sebastien Pouliot -// Jeffrey Stedfast -// -// Copyright (c) 2003 Motus Technologies Inc. (http://www.motus.com) -// Copyright (c) 2004 Novell, Inc (http://www.novell.com) -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// References -// a. NTLM Authentication Scheme for HTTP, Ronald Tschalär -// http://www.innovation.ch/java/ntlm.html -// b. The NTLM Authentication Protocol, Copyright © 2003 Eric Glass -// http://davenport.sourceforge.net/ntlm.html -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Text; - -namespace MailKit.Security.Ntlm { - class Type3Message : MessageBase - { - readonly Type2Message type2; - readonly byte[] challenge; - - public Type3Message (byte[] message, int startIndex, int length) : base (3) - { - Decode (message, startIndex, length); - type2 = null; - } - - public Type3Message (Type2Message type2, NtlmAuthLevel level, string userName, string password, string host) : base (3) - { - this.type2 = type2; - - challenge = type2.Nonce; - Domain = type2.TargetName; - Username = userName; - Password = password; - Level = level; - Host = host; - Flags = 0; - - if ((type2.Flags & NtlmFlags.NegotiateUnicode) != 0) - Flags |= NtlmFlags.NegotiateUnicode; - else - Flags |= NtlmFlags.NegotiateOem; - - if ((type2.Flags & NtlmFlags.NegotiateNtlm) != 0) - Flags |= NtlmFlags.NegotiateNtlm; - - if ((type2.Flags & NtlmFlags.NegotiateNtlm2Key) != 0) - Flags |= NtlmFlags.NegotiateNtlm2Key; - - if ((type2.Flags & NtlmFlags.NegotiateVersion) != 0) - Flags |= NtlmFlags.NegotiateVersion; - } - - ~Type3Message () - { - if (challenge != null) - Array.Clear (challenge, 0, challenge.Length); - - if (LM != null) - Array.Clear (LM, 0, LM.Length); - - if (NT != null) - Array.Clear (NT, 0, NT.Length); - } - - public NtlmAuthLevel Level { - get; set; - } - - public string Domain { - get; set; - } - - public string Host { - get; set; - } - - public string Password { - get; set; - } - - public string Username { - get; set; - } - - public byte[] LM { - get; private set; - } - - public byte[] NT { - get; set; - } - - void Decode (byte[] message, int startIndex, int length) - { - ValidateArguments (message, startIndex, length); - - Password = null; - - if (message.Length >= 64) - Flags = (NtlmFlags) BitConverterLE.ToUInt32 (message, startIndex + 60); - else - Flags = (NtlmFlags) 0x8201; - - int lmLength = BitConverterLE.ToUInt16 (message, startIndex + 12); - int lmOffset = BitConverterLE.ToUInt16 (message, startIndex + 16); - LM = new byte[lmLength]; - Buffer.BlockCopy (message, startIndex + lmOffset, LM, 0, lmLength); - - int ntLength = BitConverterLE.ToUInt16 (message, startIndex + 20); - int ntOffset = BitConverterLE.ToUInt16 (message, startIndex + 24); - NT = new byte[ntLength]; - Buffer.BlockCopy (message, startIndex + ntOffset, NT, 0, ntLength); - - int domainLength = BitConverterLE.ToUInt16 (message, startIndex + 28); - int domainOffset = BitConverterLE.ToUInt16 (message, startIndex + 32); - Domain = DecodeString (message, startIndex + domainOffset, domainLength); - - int userLength = BitConverterLE.ToUInt16 (message, startIndex + 36); - int userOffset = BitConverterLE.ToUInt16 (message, startIndex + 40); - Username = DecodeString (message, startIndex + userOffset, userLength); - - int hostLength = BitConverterLE.ToUInt16 (message, startIndex + 44); - int hostOffset = BitConverterLE.ToUInt16 (message, startIndex + 48); - Host = DecodeString (message, startIndex + hostOffset, hostLength); - - // Session key. We don't use it yet. - // int skeyLength = BitConverterLE.ToUInt16 (message, startIndex + 52); - // int skeyOffset = BitConverterLE.ToUInt16 (message, startIndex + 56); - } - - string DecodeString (byte[] buffer, int offset, int len) - { - var encoding = (Flags & NtlmFlags.NegotiateUnicode) != 0 ? Encoding.Unicode : Encoding.UTF8; - - return encoding.GetString (buffer, offset, len); - } - - byte[] EncodeString (string text) - { - if (text == null) - return new byte[0]; - - var encoding = (Flags & NtlmFlags.NegotiateUnicode) != 0 ? Encoding.Unicode : Encoding.UTF8; - - return encoding.GetBytes (text); - } - - public override byte[] Encode () - { - var target = EncodeString (Domain); - var user = EncodeString (Username); - var host = EncodeString (Host); - var payloadOffset = 64; - bool reqVersion; - byte[] lm, ntlm; - - ChallengeResponse2.Compute (type2, Level, Username, Password, Domain, out lm, out ntlm); - - if (reqVersion = (type2.Flags & NtlmFlags.NegotiateVersion) != 0) - payloadOffset += 8; - - var lmResponseLength = lm != null ? lm.Length : 0; - var ntResponseLength = ntlm != null ? ntlm.Length : 0; - - var data = PrepareMessage (payloadOffset + target.Length + user.Length + host.Length + lmResponseLength + ntResponseLength); - - // LM response - short lmResponseOffset = (short) (payloadOffset + target.Length + user.Length + host.Length); - data[12] = (byte) lmResponseLength; - data[13] = (byte) 0x00; - data[14] = data[12]; - data[15] = data[13]; - data[16] = (byte) lmResponseOffset; - data[17] = (byte) (lmResponseOffset >> 8); - - // NT response - short ntResponseOffset = (short) (lmResponseOffset + lmResponseLength); - data[20] = (byte) ntResponseLength; - data[21] = (byte) (ntResponseLength >> 8); - data[22] = data[20]; - data[23] = data[21]; - data[24] = (byte) ntResponseOffset; - data[25] = (byte) (ntResponseOffset >> 8); - - // target - short domainLength = (short) target.Length; - short domainOffset = (short) payloadOffset; - data[28] = (byte) domainLength; - data[29] = (byte) (domainLength >> 8); - data[30] = data[28]; - data[31] = data[29]; - data[32] = (byte) domainOffset; - data[33] = (byte) (domainOffset >> 8); - - // username - short userLength = (short) user.Length; - short userOffset = (short) (domainOffset + domainLength); - data[36] = (byte) userLength; - data[37] = (byte) (userLength >> 8); - data[38] = data[36]; - data[39] = data[37]; - data[40] = (byte) userOffset; - data[41] = (byte) (userOffset >> 8); - - // host - short hostLength = (short) host.Length; - short hostOffset = (short) (userOffset + userLength); - data[44] = (byte) hostLength; - data[45] = (byte) (hostLength >> 8); - data[46] = data[44]; - data[47] = data[45]; - data[48] = (byte) hostOffset; - data[49] = (byte) (hostOffset >> 8); - - // message length - short messageLength = (short) data.Length; - data[56] = (byte) messageLength; - data[57] = (byte) (messageLength >> 8); - - // options flags - data[60] = (byte) Flags; - data[61] = (byte)((uint) Flags >> 8); - data[62] = (byte)((uint) Flags >> 16); - data[63] = (byte)((uint) Flags >> 24); - - if (reqVersion) { - // encode the Windows version as Windows 10.0 - data[64] = 0x0A; - data[65] = 0x0; - - // encode the ProductBuild version - data[66] = (byte) (10586 & 0xff); - data[67] = (byte) (10586 >> 8); - - // next 3 bytes are reserved and should remain 0 - - // encode the NTLMRevisionCurrent version - data[71] = 0x0F; - } - - Buffer.BlockCopy (target, 0, data, domainOffset, target.Length); - Buffer.BlockCopy (user, 0, data, userOffset, user.Length); - Buffer.BlockCopy (host, 0, data, hostOffset, host.Length); - - if (lm != null) { - Buffer.BlockCopy (lm, 0, data, lmResponseOffset, lm.Length); - Array.Clear (lm, 0, lm.Length); - } - - if (ntlm != null) { - Buffer.BlockCopy (ntlm, 0, data, ntResponseOffset, ntlm.Length); - Array.Clear (ntlm, 0, ntlm.Length); - } - - return data; - } - } -} diff --git a/src/MailKit/Security/RandomNumberGenerator.cs b/src/MailKit/Security/RandomNumberGenerator.cs deleted file mode 100644 index eee2d5c..0000000 --- a/src/MailKit/Security/RandomNumberGenerator.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -// RandomNumberGenerator.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2019 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using Windows.Security.Cryptography; - -namespace MailKit.Security { - class RandomNumberGenerator : IDisposable - { - public static RandomNumberGenerator Create () - { - return new RandomNumberGenerator (); - } - - public void GetBytes (byte[] bytes) - { - var buffer = CryptographicBuffer.GenerateRandom ((uint) bytes.Length); - byte[] rand; - - CryptographicBuffer.CopyToByteArray (buffer, out rand); - - Array.Copy (rand, 0, bytes, 0, rand.Length); - } - - public void Dispose () - { - } - } -} diff --git a/src/MailKit/Security/SaslException.cs b/src/MailKit/Security/SaslException.cs deleted file mode 100644 index b3b78b1..0000000 --- a/src/MailKit/Security/SaslException.cs +++ /dev/null @@ -1,158 +0,0 @@ -// -// SaslException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Security; -using System.Runtime.Serialization; - -namespace MailKit.Security { - /// - /// An enumeration of the possible error codes that may be reported by a . - /// - /// - /// An enumeration of the possible error codes that may be reported by a . - /// - public enum SaslErrorCode { - /// - /// The server's challenge was too long. - /// - ChallengeTooLong, - - /// - /// The server's response contained an incomplete challenge. - /// - IncompleteChallenge, - - /// - /// The server's challenge was invalid. - /// - InvalidChallenge, - - /// - /// The server's response did not contain a challenge. - /// - MissingChallenge, - - /// - /// The server's challenge contained an incorrect hash. - /// - IncorrectHash - } - - /// - /// A SASL authentication exception. - /// - /// - /// Typically indicates an error while parsing a server's challenge token. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class SaslException : AuthenticationException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Deserializes a . - /// - /// The serialization info. - /// The streaming context. - protected SaslException (SerializationInfo info, StreamingContext context) : base (info, context) - { - ErrorCode = (SaslErrorCode) info.GetInt32 ("ErrorCode"); - Mechanism = info.GetString ("Mechanism"); - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The SASL mechanism. - /// The error code. - /// The error message. - /// - /// is null. - /// - public SaslException (string mechanism, SaslErrorCode code, string message) : base (message) - { - if (mechanism == null) - throw new ArgumentNullException (nameof (mechanism)); - - Mechanism = mechanism; - ErrorCode = code; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Serializes the state of the . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("ErrorCode", (int) ErrorCode); - info.AddValue ("Mechanism", Mechanism); - } -#endif - - /// - /// Gets the error code. - /// - /// - /// Gets the error code. - /// - /// The error code. - public SaslErrorCode ErrorCode { - get; private set; - } - - /// - /// Gets the name of the SASL mechanism that had the error. - /// - /// - /// Gets the name of the SASL mechanism that had the error. - /// - /// The name of the SASL mechanism. - public string Mechanism { - get; private set; - } - } -} diff --git a/src/MailKit/Security/SaslMechanism.cs b/src/MailKit/Security/SaslMechanism.cs deleted file mode 100644 index 0eb9c77..0000000 --- a/src/MailKit/Security/SaslMechanism.cs +++ /dev/null @@ -1,602 +0,0 @@ -// -// SaslMechanism.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; -using System.Security.Cryptography; - -#if NETSTANDARD1_3 || NETSTANDARD1_6 -using MD5 = MimeKit.Cryptography.MD5; -#endif - -namespace MailKit.Security { - /// - /// A SASL authentication mechanism. - /// - /// - /// Authenticating via a SASL mechanism may be a multi-step process. - /// To determine if the mechanism has completed the necessary steps - /// to authentication, check the after - /// each call to . - /// - public abstract class SaslMechanism - { - /// - /// The supported authentication mechanisms in order of strongest to weakest. - /// - /// - /// Used by the various clients when authenticating via SASL to determine - /// which order the SASL mechanisms supported by the server should be tried. - /// - public static readonly string[] AuthMechanismRank = { - "SCRAM-SHA-256", "SCRAM-SHA-1", "CRAM-MD5", "DIGEST-MD5", "PLAIN", "LOGIN" - }; - static readonly bool md5supported; - - static SaslMechanism () - { - try { - using (var md5 = MD5.Create ()) - md5supported = true; - } catch { - md5supported = false; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanism(NetworkCredential) instead.")] - protected SaslMechanism (Uri uri, ICredentials credentials) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - if (credentials == null) - throw new ArgumentNullException (nameof (credentials)); - - Credentials = credentials.GetCredential (uri, MechanismName); - Uri = uri; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanism(string, string) instead.")] - protected SaslMechanism (Uri uri, string userName, string password) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - if (userName == null) - throw new ArgumentNullException (nameof (userName)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - Credentials = new NetworkCredential (userName, password); - Uri = uri; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - protected SaslMechanism (NetworkCredential credentials) - { - if (credentials == null) - throw new ArgumentNullException (nameof (credentials)); - - Credentials = credentials; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - protected SaslMechanism (string userName, string password) - { - if (userName == null) - throw new ArgumentNullException (nameof (userName)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - Credentials = new NetworkCredential (userName, password); - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public abstract string MechanismName { - get; - } - - /// - /// Gets the user's credentials. - /// - /// - /// Gets the user's credentials. - /// - /// The user's credentials. - public NetworkCredential Credentials { - get; private set; - } - - /// - /// Gets whether or not the mechanism supports an initial response (SASL-IR). - /// - /// - /// SASL mechanisms that support sending an initial client response to the server - /// should return true. - /// - /// true if the mechanism supports an initial response; otherwise, false. - public virtual bool SupportsInitialResponse { - get { return false; } - } - - /// - /// Gets or sets whether the SASL mechanism has finished authenticating. - /// - /// - /// Gets or sets whether the SASL mechanism has finished authenticating. - /// - /// true if the SASL mechanism has finished authenticating; otherwise, false. - public bool IsAuthenticated { - get; protected set; - } - - /// - /// Gets whether or not a security layer was negotiated. - /// - /// - /// Gets whether or not a security layer has been negotiated by the SASL mechanism. - /// Some SASL mechanisms, such as GSSAPI, are able to negotiate security layers - /// such as integrity and confidentiality protection. - /// - /// true if a security layer was negotiated; otherwise, false. - public virtual bool NegotiatedSecurityLayer { - get { return false; } - } - - /// - /// Gets or sets the URI of the service. - /// - /// - /// Gets or sets the URI of the service. - /// - /// The URI of the service. - internal Uri Uri { - get; set; - } - - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// The next challenge response. - /// The server's challenge token. - /// The index into the token specifying where the server's challenge begins. - /// The length of the server's challenge. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// THe SASL mechanism does not support SASL-IR. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - protected abstract byte[] Challenge (byte[] token, int startIndex, int length); - - /// - /// Decodes the base64-encoded server challenge and returns the next challenge response encoded in base64. - /// - /// - /// Decodes the base64-encoded server challenge and returns the next challenge response encoded in base64. - /// - /// The next base64-encoded challenge response. - /// The server's base64-encoded challenge token. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// THe SASL mechanism does not support SASL-IR. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - public string Challenge (string token) - { - byte[] decoded = null; - int length = 0; - - if (token != null) { - try { - decoded = Convert.FromBase64String (token.Trim ()); - length = decoded.Length; - } catch (FormatException) { - } - } - - var challenge = Challenge (decoded, 0, length); - - if (challenge == null || challenge.Length == 0) - return string.Empty; - - return Convert.ToBase64String (challenge); - } - - /// - /// Resets the state of the SASL mechanism. - /// - /// - /// Resets the state of the SASL mechanism. - /// - public virtual void Reset () - { - IsAuthenticated = false; - } - - /// - /// Determines if the specified SASL mechanism is supported by MailKit. - /// - /// - /// Use this method to make sure that a SASL mechanism is supported before calling - /// . - /// - /// true if the specified SASL mechanism is supported; otherwise, false. - /// The name of the SASL mechanism. - /// - /// is null. - /// - public static bool IsSupported (string mechanism) - { - if (mechanism == null) - throw new ArgumentNullException (nameof (mechanism)); - - switch (mechanism) { - case "SCRAM-SHA-256": return true; - case "SCRAM-SHA-1": return true; - case "DIGEST-MD5": return md5supported; - case "CRAM-MD5": return md5supported; - case "XOAUTH2": return true; - case "PLAIN": return true; - case "LOGIN": return true; - case "NTLM": return true; - default: return false; - } - } - - /// - /// Create an instance of the specified SASL mechanism using the uri and credentials. - /// - /// - /// If unsure that a particular SASL mechanism is supported, you should first call - /// . - /// - /// An instance of the requested SASL mechanism if supported; otherwise null. - /// The name of the SASL mechanism. - /// The URI of the service to authenticate against. - /// The text encoding to use for the credentials. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public static SaslMechanism Create (string mechanism, Uri uri, Encoding encoding, ICredentials credentials) - { - if (mechanism == null) - throw new ArgumentNullException (nameof (mechanism)); - - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (credentials == null) - throw new ArgumentNullException (nameof (credentials)); - - var cred = credentials.GetCredential (uri, mechanism); - - switch (mechanism) { - //case "KERBEROS_V4": return null; - case "SCRAM-SHA-256": return new SaslMechanismScramSha256 (cred) { Uri = uri }; - case "SCRAM-SHA-1": return new SaslMechanismScramSha1 (cred) { Uri = uri }; - case "DIGEST-MD5": return md5supported ? new SaslMechanismDigestMd5 (cred) { Uri = uri } : null; - case "CRAM-MD5": return md5supported ? new SaslMechanismCramMd5 (cred) { Uri = uri } : null; - //case "GSSAPI": return null; - case "XOAUTH2": return new SaslMechanismOAuth2 (cred) { Uri = uri }; - case "PLAIN": return new SaslMechanismPlain (encoding, cred) { Uri = uri }; - case "LOGIN": return new SaslMechanismLogin (encoding, cred) { Uri = uri }; - case "NTLM": return new SaslMechanismNtlm (cred) { Uri = uri }; - default: return null; - } - } - - /// - /// Create an instance of the specified SASL mechanism using the uri and credentials. - /// - /// - /// If unsure that a particular SASL mechanism is supported, you should first call - /// . - /// - /// An instance of the requested SASL mechanism if supported; otherwise null. - /// The name of the SASL mechanism. - /// The URI of the service to authenticate against. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public static SaslMechanism Create (string mechanism, Uri uri, ICredentials credentials) - { - return Create (mechanism, uri, Encoding.UTF8, credentials); - } - - /// - /// Determines if the character is a non-ASCII space. - /// - /// - /// This list was obtained from http://tools.ietf.org/html/rfc3454#appendix-C.1.2 - /// - /// true if the character is a non-ASCII space; otherwise, false. - /// The character. - static bool IsNonAsciiSpace (char c) - { - switch (c) { - case '\u00A0': // NO-BREAK SPACE - case '\u1680': // OGHAM SPACE MARK - case '\u2000': // EN QUAD - case '\u2001': // EM QUAD - case '\u2002': // EN SPACE - case '\u2003': // EM SPACE - case '\u2004': // THREE-PER-EM SPACE - case '\u2005': // FOUR-PER-EM SPACE - case '\u2006': // SIX-PER-EM SPACE - case '\u2007': // FIGURE SPACE - case '\u2008': // PUNCTUATION SPACE - case '\u2009': // THIN SPACE - case '\u200A': // HAIR SPACE - case '\u200B': // ZERO WIDTH SPACE - case '\u202F': // NARROW NO-BREAK SPACE - case '\u205F': // MEDIUM MATHEMATICAL SPACE - case '\u3000': // IDEOGRAPHIC SPACE - return true; - default: - return false; - } - } - - /// - /// Determines if the character is commonly mapped to nothing. - /// - /// - /// This list was obtained from http://tools.ietf.org/html/rfc3454#appendix-B.1 - /// - /// true if the character is commonly mapped to nothing; otherwise, false. - /// The character. - static bool IsCommonlyMappedToNothing (char c) - { - switch (c) { - case '\u00AD': case '\u034F': case '\u1806': - case '\u180B': case '\u180C': case '\u180D': - case '\u200B': case '\u200C': case '\u200D': - case '\u2060': case '\uFE00': case '\uFE01': - case '\uFE02': case '\uFE03': case '\uFE04': - case '\uFE05': case '\uFE06': case '\uFE07': - case '\uFE08': case '\uFE09': case '\uFE0A': - case '\uFE0B': case '\uFE0C': case '\uFE0D': - case '\uFE0E': case '\uFE0F': case '\uFEFF': - return true; - default: - return false; - } - } - - /// - /// Determines if the character is prohibited. - /// - /// - /// This list was obtained from http://tools.ietf.org/html/rfc3454#appendix-C.3 - /// - /// true if the character is prohibited; otherwise, false. - /// The string. - /// The character index. - static bool IsProhibited (string s, int index) - { - int u = char.ConvertToUtf32 (s, index); - - // Private Use characters: http://tools.ietf.org/html/rfc3454#appendix-C.3 - if ((u >= 0xE000 && u <= 0xF8FF) || (u >= 0xF0000 && u <= 0xFFFFD) || (u >= 0x100000 && u <= 0x10FFFD)) - return true; - - // Non-character code points: http://tools.ietf.org/html/rfc3454#appendix-C.4 - if ((u >= 0xFDD0 && u <= 0xFDEF) || (u >= 0xFFFE && u <= 0xFFFF) || (u >= 0x1FFFE && u <= 0x1FFFF) || - (u >= 0x2FFFE && u <= 0x2FFFF) || (u >= 0x3FFFE && u <= 0x3FFFF) || (u >= 0x4FFFE && u <= 0x4FFFF) || - (u >= 0x5FFFE && u <= 0x5FFFF) || (u >= 0x6FFFE && u <= 0x6FFFF) || (u >= 0x7FFFE && u <= 0x7FFFF) || - (u >= 0x8FFFE && u <= 0x8FFFF) || (u >= 0x9FFFE && u <= 0x9FFFF) || (u >= 0xAFFFE && u <= 0xAFFFF) || - (u >= 0xBFFFE && u <= 0xBFFFF) || (u >= 0xCFFFE && u <= 0xCFFFF) || (u >= 0xDFFFE && u <= 0xDFFFF) || - (u >= 0xEFFFE && u <= 0xEFFFF) || (u >= 0xFFFFE && u <= 0xFFFFF) || (u >= 0x10FFFE && u <= 0x10FFFF)) - return true; - - // Surrogate code points: http://tools.ietf.org/html/rfc3454#appendix-C.5 - if (u >= 0xD800 && u <= 0xDFFF) - return true; - - // Inappropriate for plain text characters: http://tools.ietf.org/html/rfc3454#appendix-C.6 - switch (u) { - case 0xFFF9: // INTERLINEAR ANNOTATION ANCHOR - case 0xFFFA: // INTERLINEAR ANNOTATION SEPARATOR - case 0xFFFB: // INTERLINEAR ANNOTATION TERMINATOR - case 0xFFFC: // OBJECT REPLACEMENT CHARACTER - case 0xFFFD: // REPLACEMENT CHARACTER - return true; - } - - // Inappropriate for canonical representation: http://tools.ietf.org/html/rfc3454#appendix-C.7 - if (u >= 0x2FF0 && u <= 0x2FFB) - return true; - - // Change display properties or are deprecated: http://tools.ietf.org/html/rfc3454#appendix-C.8 - switch (u) { - case 0x0340: // COMBINING GRAVE TONE MARK - case 0x0341: // COMBINING ACUTE TONE MARK - case 0x200E: // LEFT-TO-RIGHT MARK - case 0x200F: // RIGHT-TO-LEFT MARK - case 0x202A: // LEFT-TO-RIGHT EMBEDDING - case 0x202B: // RIGHT-TO-LEFT EMBEDDING - case 0x202C: // POP DIRECTIONAL FORMATTING - case 0x202D: // LEFT-TO-RIGHT OVERRIDE - case 0x202E: // RIGHT-TO-LEFT OVERRIDE - case 0x206A: // INHIBIT SYMMETRIC SWAPPING - case 0x206B: // ACTIVATE SYMMETRIC SWAPPING - case 0x206C: // INHIBIT ARABIC FORM SHAPING - case 0x206D: // ACTIVATE ARABIC FORM SHAPING - case 0x206E: // NATIONAL DIGIT SHAPES - case 0x206F: // NOMINAL DIGIT SHAPES - return true; - } - - // Tagging characters: http://tools.ietf.org/html/rfc3454#appendix-C.9 - if (u == 0xE0001 || (u >= 0xE0020 && u <= 0xE007F)) - return true; - - return false; - } - - /// - /// Prepares the user name or password string. - /// - /// - /// Prepares a user name or password string according to the rules of rfc4013. - /// - /// The prepared string. - /// The string to prepare. - /// - /// is null. - /// - /// - /// contains prohibited characters. - /// - public static string SaslPrep (string s) - { - if (s == null) - throw new ArgumentNullException (nameof (s)); - - if (s.Length == 0) - return s; - - var builder = new StringBuilder (s.Length); - for (int i = 0; i < s.Length; i++) { - if (IsNonAsciiSpace (s[i])) { - // non-ASII space characters [StringPrep, C.1.2] that can be - // mapped to SPACE (U+0020). - builder.Append (' '); - } else if (IsCommonlyMappedToNothing (s[i])) { - // the "commonly mapped to nothing" characters [StringPrep, B.1] - // that can be mapped to nothing. - } else if (char.IsControl (s[i])) { - throw new ArgumentException ("Control characters are prohibited.", nameof (s)); - } else if (IsProhibited (s, i)) { - throw new ArgumentException ("One or more characters in the string are prohibited.", nameof (s)); - } else { - builder.Append (s[i]); - } - } - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - return builder.ToString ().Normalize (NormalizationForm.FormKC); -#else - return builder.ToString (); -#endif - } - - internal static string GenerateEntropy (int n) - { - var entropy = new byte[n]; - - using (var rng = RandomNumberGenerator.Create ()) - rng.GetBytes (entropy); - - return Convert.ToBase64String (entropy); - } - } -} diff --git a/src/MailKit/Security/SaslMechanismCramMd5.cs b/src/MailKit/Security/SaslMechanismCramMd5.cs deleted file mode 100644 index 9f37e45..0000000 --- a/src/MailKit/Security/SaslMechanismCramMd5.cs +++ /dev/null @@ -1,215 +0,0 @@ -// -// SaslMechanismCramMd5.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; - -#if NETSTANDARD1_3 || NETSTANDARD1_6 -using MD5 = MimeKit.Cryptography.MD5; -#else -using System.Security.Cryptography; -#endif - -namespace MailKit.Security { - /// - /// The CRAM-MD5 SASL mechanism. - /// - /// - /// A SASL mechanism based on HMAC-MD5. - /// - public class SaslMechanismCramMd5 : SaslMechanism - { - static readonly byte[] HexAlphabet = { - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // '0' -> '7' - 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, // '8' -> 'f' - }; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new CRAM-MD5 SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismCramMd5(NetworkCredential) instead.")] - public SaslMechanismCramMd5 (Uri uri, ICredentials credentials) : base (uri, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new CRAM-MD5 SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismCramMd5(string, string) instead.")] - public SaslMechanismCramMd5 (Uri uri, string userName, string password) : base (uri, userName, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new CRAM-MD5 SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - public SaslMechanismCramMd5 (NetworkCredential credentials) : base (credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new CRAM-MD5 SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismCramMd5 (string userName, string password) : base (userName, password) - { - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public override string MechanismName { - get { return "CRAM-MD5"; } - } - - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// The next challenge response. - /// The server's challenge token. - /// The index into the token specifying where the server's challenge begins. - /// The length of the server's challenge. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// The SASL mechanism does not support SASL-IR. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - protected override byte[] Challenge (byte[] token, int startIndex, int length) - { - if (IsAuthenticated) - throw new InvalidOperationException (); - - if (token == null) - throw new NotSupportedException ("CRAM-MD5 does not support SASL-IR."); - - var userName = Encoding.UTF8.GetBytes (Credentials.UserName); - var password = Encoding.UTF8.GetBytes (Credentials.Password); - var ipad = new byte[64]; - var opad = new byte[64]; - byte[] digest; - - if (password.Length > 64) { - byte[] checksum; - - using (var md5 = MD5.Create ()) - checksum = md5.ComputeHash (password); - - Array.Copy (checksum, ipad, checksum.Length); - Array.Copy (checksum, opad, checksum.Length); - } else { - Array.Copy (password, ipad, password.Length); - Array.Copy (password, opad, password.Length); - } - - Array.Clear (password, 0, password.Length); - - for (int i = 0; i < 64; i++) { - ipad[i] ^= 0x36; - opad[i] ^= 0x5c; - } - - using (var md5 = MD5.Create ()) { - md5.TransformBlock (ipad, 0, ipad.Length, null, 0); - md5.TransformFinalBlock (token, startIndex, length); - 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; - } - - var buffer = new byte[userName.Length + 1 + (digest.Length * 2)]; - int offset = 0; - - for (int i = 0; i < userName.Length; i++) - buffer[offset++] = userName[i]; - buffer[offset++] = 0x20; - for (int i = 0; i < digest.Length; i++) { - byte c = digest[i]; - - buffer[offset++] = HexAlphabet[(c >> 4) & 0x0f]; - buffer[offset++] = HexAlphabet[c & 0x0f]; - } - - IsAuthenticated = true; - - return buffer; - } - } -} diff --git a/src/MailKit/Security/SaslMechanismDigestMd5.cs b/src/MailKit/Security/SaslMechanismDigestMd5.cs deleted file mode 100644 index 8827cbb..0000000 --- a/src/MailKit/Security/SaslMechanismDigestMd5.cs +++ /dev/null @@ -1,571 +0,0 @@ -// -// SaslMechanismDigestMd5.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; -using System.Globalization; -using System.Collections.Generic; -using System.Security.Cryptography; - -#if NETSTANDARD1_3 || NETSTANDARD1_6 -using MD5 = MimeKit.Cryptography.MD5; -#endif - -namespace MailKit.Security { - /// - /// The DIGEST-MD5 SASL mechanism. - /// - /// - /// Unlike the PLAIN and LOGIN SASL mechanisms, the DIGEST-MD5 mechanism - /// provides some level of protection and should be relatively safe to - /// use even with a clear-text connection. - /// - public class SaslMechanismDigestMd5 : SaslMechanism - { - static readonly Encoding Latin1; - - enum LoginState { - Auth, - Final - } - - DigestChallenge challenge; - DigestResponse response; - internal string cnonce; - Encoding encoding; - LoginState state; - - static SaslMechanismDigestMd5 () - { - try { - Latin1 = Encoding.GetEncoding (28591); - } catch (NotSupportedException) { - Latin1 = Encoding.GetEncoding (1252); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new DIGEST-MD5 SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismDigestMd5(NetworkCredential) instead.")] - public SaslMechanismDigestMd5 (Uri uri, ICredentials credentials) : base (uri, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new DIGEST-MD5 SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismDigestMd5(string, string) instead.")] - public SaslMechanismDigestMd5 (Uri uri, string userName, string password) : base (uri, userName, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new DIGEST-MD5 SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - public SaslMechanismDigestMd5 (NetworkCredential credentials) : base (credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new DIGEST-MD5 SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismDigestMd5 (string userName, string password) : base (userName, password) - { - } - - /// - /// Gets or sets the authorization identifier. - /// - /// - /// The authorization identifier is the desired user account that the server should use - /// for all accesses. This is separate from the user name used for authentication. - /// - /// The authorization identifier. - public string AuthorizationId { - get; set; - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public override string MechanismName { - get { return "DIGEST-MD5"; } - } - - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// The next challenge response. - /// The server's challenge token. - /// The index into the token specifying where the server's challenge begins. - /// The length of the server's challenge. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// THe SASL mechanism does not support SASL-IR. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - protected override byte[] Challenge (byte[] token, int startIndex, int length) - { - if (IsAuthenticated) - throw new InvalidOperationException (); - - if (token == null) - throw new NotSupportedException ("DIGEST-MD5 does not support SASL-IR."); - - switch (state) { - case LoginState.Auth: - if (token.Length > 2048) - throw new SaslException (MechanismName, SaslErrorCode.ChallengeTooLong, "Server challenge too long."); - - challenge = DigestChallenge.Parse (Encoding.UTF8.GetString (token, startIndex, length)); - encoding = challenge.Charset != null ? Encoding.UTF8 : Latin1; - cnonce = cnonce ?? GenerateEntropy (15); - - response = new DigestResponse (challenge, encoding, Uri.Scheme, Uri.DnsSafeHost, AuthorizationId, Credentials.UserName, Credentials.Password, cnonce); - state = LoginState.Final; - - return response.Encode (encoding); - case LoginState.Final: - if (token.Length == 0) - throw new SaslException (MechanismName, SaslErrorCode.MissingChallenge, "Server response did not contain any authentication data."); - - 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."); - - 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); - if (value != expected) - throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Server response did not contain the expected hash."); - - IsAuthenticated = true; - break; - } - - return null; - } - - /// - /// Resets the state of the SASL mechanism. - /// - /// - /// Resets the state of the SASL mechanism. - /// - public override void Reset () - { - state = LoginState.Auth; - challenge = null; - response = null; - cnonce = null; - base.Reset (); - } - } - - class DigestChallenge - { - 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 HashSet Ciphers { get; private set; } - - DigestChallenge () - { - Ciphers = new HashSet (StringComparer.Ordinal); - Qop = new HashSet (StringComparer.Ordinal); - } - - static bool SkipWhiteSpace (string text, ref int index) - { - int startIndex = index; - - while (index < text.Length && char.IsWhiteSpace (text[index])) - index++; - - return index > startIndex; - } - - static string GetKey (string text, ref int index) - { - int startIndex = index; - - while (index < text.Length && !char.IsWhiteSpace (text[index]) && text[index] != '=' && text[index] != ',') - index++; - - return text.Substring (startIndex, index - startIndex); - } - - static bool TryParseQuoted (string text, ref int index, out string value) - { - var builder = new StringBuilder (); - bool escaped = false; - - value = null; - - // skip over leading '"' - index++; - - while (index < text.Length) { - if (text[index] == '\\') { - if (escaped) - builder.Append (text[index]); - - escaped = !escaped; - } else if (!escaped) { - if (text[index] == '"') - break; - - builder.Append (text[index]); - } else { - escaped = false; - } - - index++; - } - - if (index >= text.Length || text[index] != '"') - return false; - - index++; - - value = builder.ToString (); - - return true; - } - - static bool TryParseValue (string text, ref int index, out string value) - { - if (text[index] == '"') - return TryParseQuoted (text, ref index, out value); - - int startIndex = index; - - value = null; - - while (index < text.Length && !char.IsWhiteSpace (text[index]) && text[index] != ',') - index++; - - value = text.Substring (startIndex, index - startIndex); - - return true; - } - - static bool TryParseKeyValuePair (string text, ref int index, out string key, out string value) - { - value = null; - - key = GetKey (text, ref index); - - SkipWhiteSpace (text, ref index); - if (index >= text.Length || text[index] != '=') - return false; - - // skip over '=' - index++; - - SkipWhiteSpace (text, ref index); - if (index >= text.Length) - return false; - - return TryParseValue (text, ref index, out value); - } - - public static bool TryParseKeyValuePair (string text, out string key, out string value) - { - int index = 0; - - value = null; - key = null; - - SkipWhiteSpace (text, ref index); - if (index >= text.Length || !TryParseKeyValuePair (text, ref index, out key, out value)) - return false; - - return true; - } - - public static DigestChallenge Parse (string token) - { - var challenge = new DigestChallenge (); - int index = 0; - int maxbuf; - - SkipWhiteSpace (token, ref index); - - while (index < token.Length) { - string key, value; - - if (!TryParseKeyValuePair (token, ref index, out key, out value)) - throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - - switch (key.ToLowerInvariant ()) { - case "realm": - challenge.Realms = value.Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); - break; - case "nonce": - if (challenge.Nonce != null) - throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.Nonce = value; - break; - case "qop": - foreach (var qop in value.Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) - challenge.Qop.Add (qop.Trim ()); - break; - case "stale": - if (challenge.Stale.HasValue) - throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.Stale = value.ToLowerInvariant () == "true"; - break; - case "maxbuf": - if (challenge.MaxBuf.HasValue || !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)) - throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.Charset = "utf-8"; - break; - case "algorithm": - if (challenge.Algorithm != null) - throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - challenge.Algorithm = value; - break; - case "cipher": - if (challenge.Ciphers.Count > 0) - throw new SaslException ("DIGEST-MD5", SaslErrorCode.InvalidChallenge, string.Format ("Invalid SASL challenge from the server: {0}", token)); - foreach (var cipher in value.Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) - challenge.Ciphers.Add (cipher.Trim ()); - break; - } - - SkipWhiteSpace (token, ref index); - if (index < token.Length && token[index] == ',') { - index++; - - SkipWhiteSpace (token, ref index); - } - } - - return challenge; - } - } - - class DigestResponse - { - public string UserName { get; private set; } - public string Realm { 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 DigestResponse (DigestChallenge challenge, Encoding encoding, string protocol, string hostName, string authzid, string userName, string password, string cnonce) - { - UserName = userName; - - if (challenge.Realms != null && challenge.Realms.Length > 0) - Realm = challenge.Realms[0]; - else - Realm = string.Empty; - - Nonce = challenge.Nonce; - CNonce = cnonce; - Nc = 1; - - // FIXME: make sure this is supported - Qop = "auth"; - - DigestUri = string.Format ("{0}/{1}", protocol, hostName); - Algorithm = challenge.Algorithm; - Charset = challenge.Charset; - MaxBuf = challenge.MaxBuf; - AuthZid = authzid; - Cipher = null; - - Response = ComputeHash (encoding, password, true); - } - - static string HexEncode (byte[] digest) - { - var hex = new StringBuilder (); - - for (int i = 0; i < digest.Length; i++) - hex.Append (digest[i].ToString ("x2")); - - return hex.ToString (); - } - - public string ComputeHash (Encoding encoding, string password, bool client) - { - string text, a1, a2; - byte[] buf, digest; - - // compute A1 - text = string.Format ("{0}:{1}:{2}", UserName, Realm, password); - buf = encoding.GetBytes (text); - using (var md5 = MD5.Create ()) - digest = md5.ComputeHash (buf); - - using (var md5 = MD5.Create ()) { - md5.TransformBlock (digest, 0, digest.Length, null, 0); - text = string.Format (":{0}:{1}", Nonce, CNonce); - if (!string.IsNullOrEmpty (AuthZid)) - text += ":" + AuthZid; - buf = encoding.GetBytes (text); - md5.TransformFinalBlock (buf, 0, buf.Length); - a1 = HexEncode (md5.Hash); - } - - // compute A2 - text = client ? "AUTHENTICATE:" : ":"; - text += DigestUri; - - if (Qop == "auth-int" || Qop == "auth-conf") - text += ":00000000000000000000000000000000"; - - buf = encoding.GetBytes (text); - using (var md5 = MD5.Create ()) - digest = md5.ComputeHash (buf); - a2 = HexEncode (digest); - - // compute KD - text = string.Format ("{0}:{1}:{2:x8}:{3}:{4}:{5}", a1, Nonce, Nc, CNonce, Qop, a2); - buf = encoding.GetBytes (text); - using (var md5 = MD5.Create ()) - digest = md5.ComputeHash (buf); - - return HexEncode (digest); - } - - static string Quote (string text) - { - var quoted = new StringBuilder (); - - quoted.Append ("\""); - for (int i = 0; i < text.Length; i++) { - if (text[i] == '\\' || text[i] == '"') - quoted.Append ('\\'); - quoted.Append (text[i]); - } - quoted.Append ("\""); - - return quoted.ToString (); - } - - public byte[] Encode (Encoding encoding) - { - var builder = new StringBuilder (); - builder.AppendFormat ("username={0}", Quote (UserName)); - builder.AppendFormat (",realm=\"{0}\"", Realm); - builder.AppendFormat (",nonce=\"{0}\"", Nonce); - builder.AppendFormat (",cnonce=\"{0}\"", CNonce); - builder.AppendFormat (",nc={0:x8}", Nc); - builder.AppendFormat (",qop=\"{0}\"", Qop); - builder.AppendFormat (",digest-uri=\"{0}\"", DigestUri); - builder.AppendFormat (",response={0}", Response); - if (MaxBuf.HasValue) - builder.AppendFormat (CultureInfo.InvariantCulture, ",maxbuf={0}", MaxBuf.Value); - if (!string.IsNullOrEmpty (Charset)) - builder.AppendFormat (",charset={0}", Charset); - if (!string.IsNullOrEmpty (Algorithm)) - builder.AppendFormat (",algorithm={0}", Algorithm); - if (!string.IsNullOrEmpty (Cipher)) - builder.AppendFormat (",cipher=\"{0}\"", Cipher); - if (!string.IsNullOrEmpty (AuthZid)) - builder.AppendFormat (",authzid=\"{0}\"", AuthZid); - - return encoding.GetBytes (builder.ToString ()); - } - } -} diff --git a/src/MailKit/Security/SaslMechanismLogin.cs b/src/MailKit/Security/SaslMechanismLogin.cs deleted file mode 100644 index 4fc055a..0000000 --- a/src/MailKit/Security/SaslMechanismLogin.cs +++ /dev/null @@ -1,297 +0,0 @@ -// -// SaslMechanismLogin.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; - -namespace MailKit.Security { - /// - /// The LOGIN SASL mechanism. - /// - /// - /// The LOGIN SASL mechanism provides little protection over the use - /// of plain-text passwords by obscuring the user name and password within - /// individual base64-encoded blobs and should be avoided unless used in - /// combination with an SSL or TLS connection. - /// - public class SaslMechanismLogin : SaslMechanism - { - enum LoginState { - UserName, - Password - } - - readonly Encoding encoding; - LoginState state; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new LOGIN SASL context. - /// - /// The URI of the service. - /// The encoding to use for the user's credentials. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismLogin(Encoding, NetworkCredential) instead.")] - public SaslMechanismLogin (Uri uri, Encoding encoding, ICredentials credentials) : base (uri, credentials) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - this.encoding = encoding; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new LOGIN SASL context. - /// - /// The URI of the service. - /// The encoding to use for the user's credentials. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismLogin(Encoding, string, string) instead.")] - public SaslMechanismLogin (Uri uri, Encoding encoding, string userName, string password) : base (uri, userName, password) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - this.encoding = encoding; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new LOGIN SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismLogin(NetworkCredential) instead.")] - public SaslMechanismLogin (Uri uri, ICredentials credentials) : this (uri, Encoding.UTF8, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new LOGIN SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismLogin(string, string) instead.")] - public SaslMechanismLogin (Uri uri, string userName, string password) : this (uri, Encoding.UTF8, userName, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new LOGIN SASL context. - /// - /// The encoding to use for the user's credentials. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismLogin (Encoding encoding, NetworkCredential credentials) : base (credentials) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - this.encoding = encoding; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new LOGIN SASL context. - /// - /// The encoding to use for the user's credentials. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismLogin (Encoding encoding, string userName, string password) : base (userName, password) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - this.encoding = encoding; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new LOGIN SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - public SaslMechanismLogin (NetworkCredential credentials) : this (Encoding.UTF8, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new LOGIN SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismLogin (string userName, string password) : this (Encoding.UTF8, userName, password) - { - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public override string MechanismName { - get { return "LOGIN"; } - } - - /// - /// Gets whether or not the mechanism supports an initial response (SASL-IR). - /// - /// - /// SASL mechanisms that support sending an initial client response to the server - /// should return true. - /// - /// true if the mechanism supports an initial response; otherwise, false. - public override bool SupportsInitialResponse { - get { return false; } - } - - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// The next challenge response. - /// The server's challenge token. - /// The index into the token specifying where the server's challenge begins. - /// The length of the server's challenge. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// The SASL mechanism does not support SASL-IR. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - protected override byte[] Challenge (byte[] token, int startIndex, int length) - { - if (IsAuthenticated) - throw new InvalidOperationException (); - - byte[] challenge = null; - - switch (state) { - case LoginState.UserName: - if (token == null) - throw new NotSupportedException ("LOGIN does not support SASL-IR."); - - challenge = encoding.GetBytes (Credentials.UserName); - state = LoginState.Password; - break; - case LoginState.Password: - challenge = encoding.GetBytes (Credentials.Password); - IsAuthenticated = true; - break; - } - - return challenge; - } - - /// - /// Resets the state of the SASL mechanism. - /// - /// - /// Resets the state of the SASL mechanism. - /// - public override void Reset () - { - state = LoginState.UserName; - base.Reset (); - } - } -} diff --git a/src/MailKit/Security/SaslMechanismNtlm.cs b/src/MailKit/Security/SaslMechanismNtlm.cs deleted file mode 100644 index 1e7af8d..0000000 --- a/src/MailKit/Security/SaslMechanismNtlm.cs +++ /dev/null @@ -1,230 +0,0 @@ -// -// SaslMechanismNtlm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; - -using MailKit.Security.Ntlm; - -namespace MailKit.Security { - /// - /// The NTLM SASL mechanism. - /// - /// - /// A SASL mechanism based on NTLM. - /// - public class SaslMechanismNtlm : SaslMechanism - { - enum LoginState { - Initial, - Challenge - } - - LoginState state; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new NTLM SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismNtlm(NetworkCredential) instead.")] - public SaslMechanismNtlm (Uri uri, ICredentials credentials) : base (uri, credentials) - { - Level = NtlmAuthLevel.NTLMv2_only; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new NTLM SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismNtlm(string, string) instead.")] - public SaslMechanismNtlm (Uri uri, string userName, string password) : base (uri, userName, password) - { - Level = NtlmAuthLevel.NTLMv2_only; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new NTLM SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - public SaslMechanismNtlm (NetworkCredential credentials) : base (credentials) - { - Level = NtlmAuthLevel.NTLMv2_only; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new NTLM SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismNtlm (string userName, string password) : base (userName, password) - { - Level = NtlmAuthLevel.NTLMv2_only; - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public override string MechanismName { - get { return "NTLM"; } - } - - /// - /// Gets whether or not the mechanism supports an initial response (SASL-IR). - /// - /// - /// SASL mechanisms that support sending an initial client response to the server - /// should return true. - /// - /// true if the mechanism supports an initial response; otherwise, false. - public override bool SupportsInitialResponse { - get { return true; } - } - - internal NtlmAuthLevel Level { - get; set; - } - - /// - /// Gets or sets the workstation name to use for authentication. - /// - /// - /// Gets or sets the workstation name to use for authentication. - /// - /// The workstation name. - public string Workstation { - get; set; - } - - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// The next challenge response. - /// The server's challenge token. - /// The index into the token specifying where the server's challenge begins. - /// The length of the server's challenge. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - protected override byte[] Challenge (byte[] token, int startIndex, int length) - { - if (IsAuthenticated) - throw new InvalidOperationException (); - - string userName = Credentials.UserName; - string domain = Credentials.Domain; - MessageBase message = null; - - if (string.IsNullOrEmpty (domain)) { - int index = userName.IndexOf ('\\'); - if (index == -1) - index = userName.IndexOf ('/'); - - if (index >= 0) { - domain = userName.Substring (0, index); - userName = userName.Substring (index + 1); - } - } - - switch (state) { - case LoginState.Initial: - message = new Type1Message (Workstation, domain); - state = LoginState.Challenge; - break; - case LoginState.Challenge: - var password = Credentials.Password ?? string.Empty; - message = GetChallengeResponse (userName, password, token, startIndex, length); - IsAuthenticated = true; - break; - } - - return message?.Encode (); - } - - MessageBase GetChallengeResponse (string userName, string password, byte[] token, int startIndex, int length) - { - var type2 = new Type2Message (token, startIndex, length); - - return new Type3Message (type2, Level, userName, password, Workstation); - } - - /// - /// Resets the state of the SASL mechanism. - /// - /// - /// Resets the state of the SASL mechanism. - /// - public override void Reset () - { - state = LoginState.Initial; - base.Reset (); - } - } -} diff --git a/src/MailKit/Security/SaslMechanismOAuth2.cs b/src/MailKit/Security/SaslMechanismOAuth2.cs deleted file mode 100644 index ff8dc19..0000000 --- a/src/MailKit/Security/SaslMechanismOAuth2.cs +++ /dev/null @@ -1,179 +0,0 @@ -// -// SaslMechanismOAuth2.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; - -namespace MailKit.Security { - /// - /// The OAuth2 SASL mechanism. - /// - /// - /// A SASL mechanism used by Google that makes use of a short-lived - /// OAuth 2.0 access token. - /// - public class SaslMechanismOAuth2 : SaslMechanism - { - const string AuthBearer = "auth=Bearer "; - const string UserEquals = "user="; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new XOAUTH2 SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismOAuth2(NetworkCredential) instead.")] - public SaslMechanismOAuth2 (Uri uri, ICredentials credentials) : base (uri, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new XOAUTH2 SASL context. - /// - /// The URI of the service. - /// The user name. - /// The auth token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismOAuth2(string, string) instead.")] - public SaslMechanismOAuth2 (Uri uri, string userName, string auth_token) : base (uri, userName, auth_token) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new XOAUTH2 SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - public SaslMechanismOAuth2 (NetworkCredential credentials) : base (credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new XOAUTH2 SASL context. - /// - /// The user name. - /// The auth token. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismOAuth2 (string userName, string auth_token) : base (userName, auth_token) - { - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public override string MechanismName { - get { return "XOAUTH2"; } - } - - /// - /// Gets whether or not the mechanism supports an initial response (SASL-IR). - /// - /// - /// SASL mechanisms that support sending an initial client response to the server - /// should return true. - /// - /// true if the mechanism supports an initial response; otherwise, false. - public override bool SupportsInitialResponse { - get { return true; } - } - - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// The next challenge response. - /// The server's challenge token. - /// The index into the token specifying where the server's challenge begins. - /// The length of the server's challenge. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - protected override byte[] Challenge (byte[] token, int startIndex, int length) - { - if (IsAuthenticated) - throw new InvalidOperationException (); - - var authToken = Credentials.Password; - var userName = Credentials.UserName; - int index = 0; - - var buf = new byte[UserEquals.Length + userName.Length + AuthBearer.Length + authToken.Length + 3]; - for (int i = 0; i < UserEquals.Length; i++) - buf[index++] = (byte) UserEquals[i]; - for (int i = 0; i < userName.Length; i++) - buf[index++] = (byte) userName[i]; - buf[index++] = 1; - for (int i = 0; i < AuthBearer.Length; i++) - buf[index++] = (byte) AuthBearer[i]; - for (int i = 0; i < authToken.Length; i++) - buf[index++] = (byte) authToken[i]; - buf[index++] = 1; - buf[index++] = 1; - - IsAuthenticated = true; - - return buf; - } - } -} diff --git a/src/MailKit/Security/SaslMechanismPlain.cs b/src/MailKit/Security/SaslMechanismPlain.cs deleted file mode 100644 index c37ccfa..0000000 --- a/src/MailKit/Security/SaslMechanismPlain.cs +++ /dev/null @@ -1,293 +0,0 @@ -// -// SaslMechanismPlain.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; - -namespace MailKit.Security { - /// - /// The PLAIN SASL mechanism. - /// - /// - /// The PLAIN SASL mechanism provides little protection over the use - /// of plain-text passwords by combining the user name and password and - /// obscuring them within a base64-encoded blob and should be avoided - /// unless used in combination with an SSL or TLS connection. - /// - public class SaslMechanismPlain : SaslMechanism - { - readonly Encoding encoding; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new PLAIN SASL context. - /// - /// The URI of the service. - /// The encoding to use for the user's credentials. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismPlain(Encoding, NetworkCredential) instead.")] - public SaslMechanismPlain (Uri uri, Encoding encoding, ICredentials credentials) : base (uri, credentials) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - this.encoding = encoding; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new PLAIN SASL context. - /// - /// The URI of the service. - /// The encoding to use for the user's credentials. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismPlain(Encoding, string, string) instead.")] - public SaslMechanismPlain (Uri uri, Encoding encoding, string userName, string password) : base (uri, userName, password) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - this.encoding = encoding; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new PLAIN SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismPlain(NetworkCredential) instead.")] - public SaslMechanismPlain (Uri uri, ICredentials credentials) : this (uri, Encoding.UTF8, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new PLAIN SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismPlain(string, string) instead.")] - public SaslMechanismPlain (Uri uri, string userName, string password) : this (uri, Encoding.UTF8, userName, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new PLAIN SASL context. - /// - /// The encoding to use for the user's credentials. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismPlain (Encoding encoding, NetworkCredential credentials) : base (credentials) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - this.encoding = encoding; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new PLAIN SASL context. - /// - /// The encoding to use for the user's credentials. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismPlain (Encoding encoding, string userName, string password) : base (userName, password) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - this.encoding = encoding; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new PLAIN SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - public SaslMechanismPlain (NetworkCredential credentials) : this (Encoding.UTF8, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new PLAIN SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismPlain (string userName, string password) : this (Encoding.UTF8, userName, password) - { - } - - /// - /// Gets or sets the authorization identifier. - /// - /// - /// The authorization identifier is the desired user account that the server should use - /// for all accesses. This is separate from the user name used for authentication. - /// - /// The authorization identifier. - public string AuthorizationId { - get; set; - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public override string MechanismName { - get { return "PLAIN"; } - } - - /// - /// Gets whether or not the mechanism supports an initial response (SASL-IR). - /// - /// - /// SASL mechanisms that support sending an initial client response to the server - /// should return true. - /// - /// true if the mechanism supports an initial response; otherwise, false. - public override bool SupportsInitialResponse { - get { return true; } - } - - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// The next challenge response. - /// The server's challenge token. - /// The index into the token specifying where the server's challenge begins. - /// The length of the server's challenge. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - protected override byte[] Challenge (byte[] token, int startIndex, int length) - { - if (IsAuthenticated) - throw new InvalidOperationException (); - - var authzid = encoding.GetBytes (AuthorizationId ?? string.Empty); - var authcid = encoding.GetBytes (Credentials.UserName); - var passwd = encoding.GetBytes (Credentials.Password); - var buffer = new byte[authzid.Length + authcid.Length + passwd.Length + 2]; - int offset = 0; - - for (int i = 0; i < authzid.Length; i++) - buffer[offset++] = authzid[i]; - - buffer[offset++] = 0; - for (int i = 0; i < authcid.Length; i++) - buffer[offset++] = authcid[i]; - - buffer[offset++] = 0; - for (int i = 0; i < passwd.Length; i++) - buffer[offset++] = passwd[i]; - - Array.Clear (passwd, 0, passwd.Length); - - IsAuthenticated = true; - - return buffer; - } - } -} diff --git a/src/MailKit/Security/SaslMechanismScramBase.cs b/src/MailKit/Security/SaslMechanismScramBase.cs deleted file mode 100644 index 60d87fc..0000000 --- a/src/MailKit/Security/SaslMechanismScramBase.cs +++ /dev/null @@ -1,376 +0,0 @@ -// -// SaslMechanismScramBase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Text; -using System.Globalization; -using System.Collections.Generic; -using System.Security.Cryptography; - -namespace MailKit.Security { - /// - /// The base class for SCRAM-based SASL mechanisms. - /// - /// - /// SCRAM-based SASL mechanisms are salted challenge/response authentication mechanisms. - /// - public abstract class SaslMechanismScramBase : SaslMechanism - { - enum LoginState { - Initial, - Final, - Validate - } - - internal string cnonce; - string client, server; - byte[] salted, auth; - LoginState state; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-based SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismScramBase(NetworkCredential) instead.")] - protected SaslMechanismScramBase (Uri uri, ICredentials credentials) : base (uri, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-based SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismScramBase(string, string) instead.")] - protected SaslMechanismScramBase (Uri uri, string userName, string password) : base (uri, userName, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-based SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - protected SaslMechanismScramBase (NetworkCredential credentials) : base (credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-based SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - protected SaslMechanismScramBase (string userName, string password) : base (userName, password) - { - } - - /// - /// Gets whether or not the mechanism supports an initial response (SASL-IR). - /// - /// - /// SASL mechanisms that support sending an initial client response to the server - /// should return true. - /// - /// true if the mechanism supports an initial response; otherwise, false. - public override bool SupportsInitialResponse { - get { return true; } - } - - static string Normalize (string str) - { - var builder = new StringBuilder (); - var prepared = SaslPrep (str); - - for (int i = 0; i < prepared.Length; i++) { - switch (prepared[i]) { - case ',': builder.Append ("=2C"); break; - case '=': builder.Append ("=3D"); break; - default: - builder.Append (prepared[i]); - break; - } - } - - return builder.ToString (); - } - - /// - /// Create the HMAC context. - /// - /// - /// Creates the HMAC context using the secret key. - /// - /// The HMAC context. - /// The secret key. - protected abstract KeyedHashAlgorithm CreateHMAC (byte[] key); - - /// - /// Apply the HMAC keyed algorithm. - /// - /// - /// HMAC(key, str): Apply the HMAC keyed hash algorithm (defined in - /// [RFC2104]) using the octet string represented by "key" as the key - /// and the octet string "str" as the input string. The size of the - /// result is the hash result size for the hash function in use. For - /// example, it is 20 octets for SHA-1 (see [RFC3174]). - /// - /// The results of the HMAC keyed algorithm. - /// The key. - /// The string. - byte[] HMAC (byte[] key, byte[] str) - { - using (var hmac = CreateHMAC (key)) - return hmac.ComputeHash (str); - } - - /// - /// Apply the cryptographic hash function. - /// - /// - /// H(str): Apply the cryptographic hash function to the octet string - /// "str", producing an octet string as a result. The size of the - /// result depends on the hash result size for the hash function in - /// use. - /// - /// The results of the hash. - /// The string. - protected abstract byte[] Hash (byte[] str); - - /// - /// Apply the exclusive-or operation to combine two octet strings. - /// - /// - /// Apply the exclusive-or operation to combine the octet string - /// on the left of this operator with the octet string on the right of - /// this operator. The length of the output and each of the two - /// inputs will be the same for this use. - /// - /// The alpha component. - /// The blue component. - static void Xor (byte[] a, byte[] b) - { - for (int i = 0; i < a.Length; i++) - a[i] = (byte) (a[i] ^ b[i]); - } - - // Hi(str, salt, i): - // - // U1 := HMAC(str, salt + INT(1)) - // U2 := HMAC(str, U1) - // ... - // Ui-1 := HMAC(str, Ui-2) - // Ui := HMAC(str, Ui-1) - // - // Hi := U1 XOR U2 XOR ... XOR Ui - // - // where "i" is the iteration count, "+" is the string concatenation - // operator, and INT(g) is a 4-octet encoding of the integer g, most - // significant octet first. - // - // Hi() is, essentially, PBKDF2 [RFC2898] with HMAC() as the - // pseudorandom function (PRF) and with dkLen == output length of - // HMAC() == output length of H(). - byte[] Hi (byte[] str, byte[] salt, int count) - { - using (var hmac = CreateHMAC (str)) { - var salt1 = new byte[salt.Length + 4]; - byte[] hi, u1; - - Buffer.BlockCopy (salt, 0, salt1, 0, salt.Length); - salt1[salt1.Length - 1] = (byte) 1; - - hi = u1 = hmac.ComputeHash (salt1); - - for (int i = 1; i < count; i++) { - var u2 = hmac.ComputeHash (u1); - Xor (hi, u2); - u1 = u2; - } - - return hi; - } - } - - static Dictionary ParseServerChallenge (string challenge) - { - var results = new Dictionary (); - - foreach (var pair in challenge.Split (',')) { - if (pair.Length < 2 || pair[1] != '=') - continue; - - results.Add (pair[0], pair.Substring (2)); - } - - return results; - } - - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// - /// Parses the server's challenge token and returns the next challenge response. - /// - /// The next challenge response. - /// The server's challenge token. - /// The index into the token specifying where the server's challenge begins. - /// The length of the server's challenge. - /// - /// The SASL mechanism is already authenticated. - /// - /// - /// An error has occurred while parsing the server's challenge token. - /// - protected override byte[] Challenge (byte[] token, int startIndex, int length) - { - if (IsAuthenticated) - throw new InvalidOperationException (); - - byte[] response, signature; - - switch (state) { - case LoginState.Initial: - cnonce = cnonce ?? GenerateEntropy (18); - client = "n=" + Normalize (Credentials.UserName) + ",r=" + cnonce; - response = Encoding.UTF8.GetBytes ("n,," + client); - state = LoginState.Final; - break; - case LoginState.Final: - server = Encoding.UTF8.GetString (token, startIndex, length); - var tokens = ParseServerChallenge (server); - string salt, nonce, iterations; - int count; - - if (!tokens.TryGetValue ('s', out salt)) - throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Challenge did not contain a salt."); - - if (!tokens.TryGetValue ('r', out nonce)) - throw new SaslException (MechanismName, SaslErrorCode.IncompleteChallenge, "Challenge did not contain a nonce."); - - 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)) - throw new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, "Challenge contained an invalid nonce."); - - if (!int.TryParse (iterations, NumberStyles.None, CultureInfo.InvariantCulture, out count) || count < 1) - throw new SaslException (MechanismName, SaslErrorCode.InvalidChallenge, "Challenge contained an invalid iteration count."); - - var password = Encoding.UTF8.GetBytes (SaslPrep (Credentials.Password)); - salted = Hi (password, Convert.FromBase64String (salt), count); - Array.Clear (password, 0, password.Length); - - var withoutProof = "c=" + Convert.ToBase64String (Encoding.ASCII.GetBytes ("n,,")) + ",r=" + nonce; - auth = Encoding.UTF8.GetBytes (client + "," + server + "," + withoutProof); - - var key = HMAC (salted, Encoding.ASCII.GetBytes ("Client Key")); - signature = HMAC (Hash (key), auth); - Xor (key, signature); - - response = Encoding.UTF8.GetBytes (withoutProof + ",p=" + Convert.ToBase64String (key)); - state = LoginState.Validate; - break; - case LoginState.Validate: - 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); - - if (signature.Length != calculated.Length) - throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Challenge contained a signature with an invalid length."); - - for (int i = 0; i < signature.Length; i++) { - if (signature[i] != calculated[i]) - throw new SaslException (MechanismName, SaslErrorCode.IncorrectHash, "Challenge contained an invalid signatire."); - } - - IsAuthenticated = true; - response = new byte[0]; - break; - default: - throw new IndexOutOfRangeException ("state"); - } - - return response; - } - - /// - /// Resets the state of the SASL mechanism. - /// - /// - /// Resets the state of the SASL mechanism. - /// - public override void Reset () - { - state = LoginState.Initial; - client = null; - server = null; - salted = null; - cnonce = null; - auth = null; - - base.Reset (); - } - } -} diff --git a/src/MailKit/Security/SaslMechanismScramSha1.cs b/src/MailKit/Security/SaslMechanismScramSha1.cs deleted file mode 100644 index 706186f..0000000 --- a/src/MailKit/Security/SaslMechanismScramSha1.cs +++ /dev/null @@ -1,151 +0,0 @@ -// -// SaslMechanismScramSha1.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Security.Cryptography; - -namespace MailKit.Security { - /// - /// The SCRAM-SHA-1 SASL mechanism. - /// - /// - /// A salted challenge/response SASL mechanism that uses the HMAC SHA-1 algorithm. - /// - public class SaslMechanismScramSha1 : SaslMechanismScramBase - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-SHA-1 SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismScramSha1(NetworkCredential) instead.")] - public SaslMechanismScramSha1 (Uri uri, ICredentials credentials) : base (uri, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-SHA-1 SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismScramSha1(string, string) instead.")] - public SaslMechanismScramSha1 (Uri uri, string userName, string password) : base (uri, userName, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-SHA-1 SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - public SaslMechanismScramSha1 (NetworkCredential credentials) : base (credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-SHA-1 SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismScramSha1 (string userName, string password) : base (userName, password) - { - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public override string MechanismName { - get { return "SCRAM-SHA-1"; } - } - - /// - /// Create the HMAC context. - /// - /// - /// Creates the HMAC context using the secret key. - /// - /// The HMAC context. - /// The secret key. - protected override KeyedHashAlgorithm CreateHMAC (byte[] key) - { - return new HMACSHA1 (key); - } - - /// - /// Apply the cryptographic hash function. - /// - /// - /// H(str): Apply the cryptographic hash function to the octet string - /// "str", producing an octet string as a result. The size of the - /// result depends on the hash result size for the hash function in - /// use. - /// - /// The results of the hash. - /// The string. - protected override byte[] Hash (byte[] str) - { - using (var sha1 = SHA1.Create ()) - return sha1.ComputeHash (str); - } - } -} diff --git a/src/MailKit/Security/SaslMechanismScramSha256.cs b/src/MailKit/Security/SaslMechanismScramSha256.cs deleted file mode 100644 index f53ec0a..0000000 --- a/src/MailKit/Security/SaslMechanismScramSha256.cs +++ /dev/null @@ -1,151 +0,0 @@ -// -// SaslMechanismScramSha256.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Net; -using System.Security.Cryptography; - -namespace MailKit.Security { - /// - /// The SCRAM-SHA-256 SASL mechanism. - /// - /// - /// A salted challenge/response SASL mechanism that uses the HMAC SHA-256 algorithm. - /// - public class SaslMechanismScramSha256 : SaslMechanismScramBase - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-SHA-256 SASL context. - /// - /// The URI of the service. - /// The user's credentials. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismScramSha256(NetworkCredential) instead.")] - public SaslMechanismScramSha256 (Uri uri, ICredentials credentials) : base (uri, credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-SHA-256 SASL context. - /// - /// The URI of the service. - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use SaslMechanismScramSha256(string, string) instead.")] - public SaslMechanismScramSha256 (Uri uri, string userName, string password) : base (uri, userName, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-SHA-256 SASL context. - /// - /// The user's credentials. - /// - /// is null. - /// - public SaslMechanismScramSha256 (NetworkCredential credentials) : base (credentials) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new SCRAM-SHA-256 SASL context. - /// - /// The user name. - /// The password. - /// - /// is null. - /// -or- - /// is null. - /// - public SaslMechanismScramSha256 (string userName, string password) : base (userName, password) - { - } - - /// - /// Gets the name of the mechanism. - /// - /// - /// Gets the name of the mechanism. - /// - /// The name of the mechanism. - public override string MechanismName { - get { return "SCRAM-SHA-256"; } - } - - /// - /// Create the HMAC context. - /// - /// - /// Creates the HMAC context using the secret key. - /// - /// The HMAC context. - /// The secret key. - protected override KeyedHashAlgorithm CreateHMAC (byte[] key) - { - return new HMACSHA256 (key); - } - - /// - /// Apply the cryptographic hash function. - /// - /// - /// H(str): Apply the cryptographic hash function to the octet string - /// "str", producing an octet string as a result. The size of the - /// result depends on the hash result size for the hash function in - /// use. - /// - /// The results of the hash. - /// The string. - protected override byte[] Hash (byte[] str) - { - using (var sha256 = SHA256.Create ()) - return sha256.ComputeHash (str); - } - } -} diff --git a/src/MailKit/Security/SecureSocketOptions.cs b/src/MailKit/Security/SecureSocketOptions.cs deleted file mode 100644 index a30205d..0000000 --- a/src/MailKit/Security/SecureSocketOptions.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// SecureSocketOptions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit.Security { - /// - /// Secure socket options. - /// - /// - /// Provides a way of specifying the SSL and/or TLS encryption that - /// should be used for a connection. - /// - public enum SecureSocketOptions { - /// - /// No SSL or TLS encryption should be used. - /// - None, - - /// - /// Allow the to decide which SSL or TLS - /// options to use (default). If the server does not support SSL or TLS, - /// then the connection will continue without any encryption. - /// - Auto, - - /// - /// The connection should use SSL or TLS encryption immediately. - /// - SslOnConnect, - - /// - /// Elevates the connection to use TLS encryption immediately after - /// reading the greeting and capabilities of the server. If the server - /// does not support the STARTTLS extension, then the connection will - /// fail and a will be thrown. - /// - StartTls, - - /// - /// Elevates the connection to use TLS encryption immediately after - /// reading the greeting and capabilities of the server, but only if - /// the server supports the STARTTLS extension. - /// - StartTlsWhenAvailable, - } -} diff --git a/src/MailKit/Security/SslHandshakeException.cs b/src/MailKit/Security/SslHandshakeException.cs deleted file mode 100644 index 5a3c0b5..0000000 --- a/src/MailKit/Security/SslHandshakeException.cs +++ /dev/null @@ -1,305 +0,0 @@ -// -// SslHandshakeException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Net.Security; -using System.Collections.Generic; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif -using System.Security.Cryptography.X509Certificates; - -namespace MailKit.Security -{ - /// - /// The exception that is thrown when there is an error during the SSL/TLS handshake. - /// - /// - /// The exception that is thrown when there is an error during the SSL/TLS handshake. - /// When this exception occurrs, it typically means that the IMAP, POP3 or SMTP server that - /// you are connecting to is using an SSL certificate that is either expired or untrusted by - /// your system. - /// Often times, mail servers will use self-signed certificates instead of using a certificate - /// that has been signed by a trusted Certificate Authority. When your system is unable to validate - /// the mail server's certificate because it is not signed by a known and trusted Certificate Authority, - /// this exception will occur. - /// You can work around this problem by supplying a custom - /// and setting it on the client's property. - /// Most likely, you'll want to compare the thumbprint of the server's certificate with a known - /// value and/or prompt the user to accept the certificate (similar to what you've probably seen web - /// browsers do when they encounter untrusted certificates). - /// -#if SERIALIZABLE - [Serializable] -#endif - public class SslHandshakeException : Exception - { - const string SslHandshakeHelpLink = "https://github.com/jstedfast/MailKit/blob/master/FAQ.md#SslHandshakeException"; - const string DefaultMessage = "An error occurred while attempting to establish an SSL or TLS connection."; - -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new from the seriaized data. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - protected SslHandshakeException (SerializationInfo info, StreamingContext context) : base (info, context) - { - var base64 = info.GetString ("ServerCertificate"); - - if (base64 != null) - ServerCertificate = new X509Certificate2 (Convert.FromBase64String (base64)); - - base64 = info.GetString ("RootCertificateAuthority"); - - if (base64 != null) - RootCertificateAuthority = new X509Certificate2 (Convert.FromBase64String (base64)); - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// An inner exception. - public SslHandshakeException (string message, Exception innerException) : base (message, innerException) - { - HelpLink = SslHandshakeHelpLink; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public SslHandshakeException (string message) : base (message) - { - HelpLink = SslHandshakeHelpLink; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public SslHandshakeException () : base (DefaultMessage) - { - HelpLink = SslHandshakeHelpLink; - } - - /// - /// Get the server's SSL certificate. - /// - /// - /// Gets the server's SSL certificate, if it is available. - /// - /// The server's SSL certificate. - public X509Certificate ServerCertificate { - get; private set; - } - - /// - /// Get the certificate for the Root Certificate Authority. - /// - /// - /// Gets the certificate for the Root Certificate Authority, if it is available. - /// - /// The Root Certificate Authority certificate. - public X509Certificate RootCertificateAuthority { - get; private set; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Sets the - /// with information about the exception. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - if (ServerCertificate != null) - info.AddValue ("ServerCertificate", Convert.ToBase64String (ServerCertificate.GetRawCertData ())); - else - info.AddValue ("ServerCertificate", null, typeof (string)); - - if (RootCertificateAuthority != null) - info.AddValue ("RootCertificateAuthority", Convert.ToBase64String (RootCertificateAuthority.GetRawCertData ())); - else - info.AddValue ("RootCertificateAuthority", null, typeof (string)); - } -#endif - - internal static SslHandshakeException Create (MailService client, Exception ex, bool starttls) - { - var message = new StringBuilder (DefaultMessage); - var aggregate = ex as AggregateException; - X509Certificate certificate = null; - X509Certificate root = null; - - if (aggregate != null) { - aggregate = aggregate.Flatten (); - - if (aggregate.InnerExceptions.Count == 1) - ex = aggregate.InnerExceptions[0]; - else - ex = aggregate; - } - - message.AppendLine (); - message.AppendLine (); - - var validationInfo = client?.SslCertificateValidationInfo; - if (validationInfo != null) { - client.SslCertificateValidationInfo = null; - - int rootIndex = validationInfo.ChainElements.Count - 1; - if (rootIndex > 0) - root = validationInfo.ChainElements[rootIndex].Certificate; - certificate = validationInfo.Certificate; - - if ((validationInfo.SslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0) { - message.AppendLine ("The SSL certificate for the server was not available."); - } else if ((validationInfo.SslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) { - message.AppendLine ("The host name did not match the name given in the server's SSL certificate."); - } else { - message.AppendLine ("The server's SSL certificate could not be validated for the following reasons:"); - - bool haveReason = false; - - for (int chainIndex = 0; chainIndex < validationInfo.ChainElements.Count; chainIndex++) { - var element = validationInfo.ChainElements[chainIndex]; - - if (element.ChainElementStatus == null || element.ChainElementStatus.Length == 0) - continue; - - if (chainIndex == 0) { - message.AppendLine ("\u2022 The server certificate has the following errors:"); - } else if (chainIndex == rootIndex) { - message.AppendLine ("\u2022 The root certificate has the following errors:"); - } else { - message.AppendLine ("\u2022 An intermediate certificate has the following errors:"); - } - - foreach (var status in element.ChainElementStatus) - message.AppendFormat (" \u2022 {0}{1}", status.StatusInformation, Environment.NewLine); - - haveReason = true; - } - - // Note: Because Mono does not include any elements in the chain (at least on macOS), we need - // to find the inner-most exception and append its Message. - if (!haveReason) { - var innerException = ex; - - while (innerException.InnerException != null) - innerException = innerException.InnerException; - - message.AppendLine ("\u2022 " + innerException.Message); - } - } - } else { - message.AppendLine ("This usually means that the SSL certificate presented by the server is not trusted by the system for one or more of"); - message.AppendLine ("the following reasons:"); - message.AppendLine (); - message.AppendLine ("1. The server is using a self-signed certificate which cannot be verified."); - message.AppendLine ("2. The local system is missing a Root or Intermediate certificate needed to verify the server's certificate."); - message.AppendLine ("3. A Certificate Authority CRL server for one or more of the certificates in the chain is temporarily unavailable."); - message.AppendLine ("4. The certificate presented by the server is expired or invalid."); - message.AppendLine (); - if (!starttls) { - message.AppendLine ("Another possibility is that you are trying to connect to a port which does not support SSL/TLS."); - message.AppendLine (); - } - message.AppendLine ("It is also possible that the set of SSL/TLS protocols supported by the client and server do not match."); - message.AppendLine (); - message.AppendLine ("See " + SslHandshakeHelpLink + " for possible solutions."); - } - - return new SslHandshakeException (message.ToString (), ex) { ServerCertificate = certificate, RootCertificateAuthority = root }; - } - } - - class SslChainElement - { - public readonly X509Certificate Certificate; - public readonly X509ChainStatus[] ChainElementStatus; - public readonly string Information; - - public SslChainElement (X509ChainElement element) - { - Certificate = new X509Certificate2 (element.Certificate.RawData); - ChainElementStatus = element.ChainElementStatus; - Information = element.Information; - } - } - - class SslCertificateValidationInfo - { - public readonly List ChainElements; - public readonly X509ChainStatus[] ChainStatus; - public readonly SslPolicyErrors SslPolicyErrors; - public readonly X509Certificate Certificate; - public readonly string Host; - - public SslCertificateValidationInfo (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { - Certificate = new X509Certificate2 (certificate.Export (X509ContentType.Cert)); - ChainElements = new List (); - SslPolicyErrors = sslPolicyErrors; - ChainStatus = chain.ChainStatus; - Host = sender as string; - - // Note: we need to copy the ChainElements because the chain will be destroyed - foreach (var element in chain.ChainElements) - ChainElements.Add (new SslChainElement (element)); - } - } -} diff --git a/src/MailKit/ServiceNotAuthenticatedException.cs b/src/MailKit/ServiceNotAuthenticatedException.cs deleted file mode 100644 index a3b032b..0000000 --- a/src/MailKit/ServiceNotAuthenticatedException.cs +++ /dev/null @@ -1,97 +0,0 @@ -// -// ServiceNotAuthenticatedException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit { - /// - /// The exception that is thrown when the is not authenticated. - /// - /// - /// This exception is thrown when an operation on a service could not be completed - /// due to the service not being authenticated. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class ServiceNotAuthenticatedException : InvalidOperationException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Deserializes a . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected ServiceNotAuthenticatedException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The inner exception. - public ServiceNotAuthenticatedException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public ServiceNotAuthenticatedException (string message) : base (message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public ServiceNotAuthenticatedException () - { - } - } -} diff --git a/src/MailKit/ServiceNotConnectedException.cs b/src/MailKit/ServiceNotConnectedException.cs deleted file mode 100644 index b21e76f..0000000 --- a/src/MailKit/ServiceNotConnectedException.cs +++ /dev/null @@ -1,97 +0,0 @@ -// -// ServiceNotConnectedException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MailKit { - /// - /// The exception that is thrown when the is not connected. - /// - /// - /// This exception is thrown when an operation on a service could not be completed - /// due to the service not being connected. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class ServiceNotConnectedException : InvalidOperationException - { -#if SERIALIZABLE - /// - /// Initializes a new instance of the class. - /// - /// - /// Deserializes a . - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecuritySafeCritical] - protected ServiceNotConnectedException (SerializationInfo info, StreamingContext context) : base (info, context) - { - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The inner exception. - public ServiceNotConnectedException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public ServiceNotConnectedException (string message) : base (message) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - public ServiceNotConnectedException () - { - } - } -} diff --git a/src/MailKit/SpecialFolder.cs b/src/MailKit/SpecialFolder.cs deleted file mode 100644 index f746ebb..0000000 --- a/src/MailKit/SpecialFolder.cs +++ /dev/null @@ -1,75 +0,0 @@ -// -// SpecialFolder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit { - /// - /// An enumeration of special folders. - /// - /// - /// An enumeration of special folders. - /// - public enum SpecialFolder { - /// - /// The special folder containing an aggregate of all messages. - /// - All, - - /// - /// The special folder that contains archived messages. - /// - Archive, - - /// - /// The special folder that contains message drafts. - /// - Drafts, - - /// - /// The special folder that contains flagged messages. - /// - Flagged, - - /// - /// The special folder that contains important messages. - /// - Important, - - /// - /// The special folder that contains spam messages. - /// - Junk, - - /// - /// The special folder that contains sent messages. - /// - Sent, - - /// - /// The special folder that contains deleted messages. - /// - Trash - } -} diff --git a/src/MailKit/StatusItems.cs b/src/MailKit/StatusItems.cs deleted file mode 100644 index 97f11c7..0000000 --- a/src/MailKit/StatusItems.cs +++ /dev/null @@ -1,88 +0,0 @@ -// -// StatusItems.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MailKit { - /// - /// Status items. - /// - /// - /// Used with - /// - [Flags] - public enum StatusItems { - /// - /// No status requested. - /// - None = 0, - - /// - /// Updates . - /// - Count = 1 << 0, - - /// - /// Updates . - /// - Recent = 1 << 1, - - /// - /// Updates . - /// - UidNext = 1 << 2, - - /// - /// Updates . - /// - UidValidity = 1 << 3, - - /// - /// Updates . - /// - Unread = 1 << 4, - - /// - /// Updates . - /// - HighestModSeq = 1 << 5, - - /// - /// Updates . - /// - AppendLimit = 1 << 6, - - /// - /// Updates . - /// - Size = 1 << 7, - - /// - /// Updates . - /// - MailboxId = 1 << 8, - } -} diff --git a/src/MailKit/ThreadingAlgorithm.cs b/src/MailKit/ThreadingAlgorithm.cs deleted file mode 100644 index 8c5e223..0000000 --- a/src/MailKit/ThreadingAlgorithm.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// ThreadingAlgorithm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MailKit { - /// - /// An enumeration of threading algorithms. - /// - /// - /// A threading algorithm is used to group messages and their - /// replies together. - /// - public enum ThreadingAlgorithm { - /// - /// Thread messages based on their Subject headers. - /// - OrderedSubject, - - /// - /// Threads messages based on their References, In-Reply-To, and Message-Id headers. - /// This algorithm is far better than but is also more - /// expensive to calculate. - /// - References, - } -} diff --git a/src/MailKit/UniqueId.cs b/src/MailKit/UniqueId.cs deleted file mode 100644 index 1c2dd3a..0000000 --- a/src/MailKit/UniqueId.cs +++ /dev/null @@ -1,428 +0,0 @@ -// -// UniqueId.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Globalization; - -namespace MailKit { - /// - /// A unique identifier. - /// - /// - /// Represents a unique identifier for messages in a . - /// - public struct UniqueId : IComparable, IEquatable - { - /// - /// The invalid value. - /// - /// - /// The invalid value. - /// - public static readonly UniqueId Invalid; - - /// - /// The minimum value. - /// - /// - /// The minimum value. - /// - public static readonly UniqueId MinValue = new UniqueId (1); - - /// - /// The maximum value. - /// - /// - /// The maximum value. - /// - public static readonly UniqueId MaxValue = new UniqueId (uint.MaxValue); - - readonly uint validity; - readonly uint id; - - /// - /// Initializes a new instance of the struct. - /// - /// - /// Creates a new with the specified validity and value. - /// - /// The uid validity. - /// The unique identifier. - /// - /// is 0. - /// - public UniqueId (uint validity, uint id) - { - if (id == 0) - throw new ArgumentOutOfRangeException (nameof (id)); - - this.validity = validity; - this.id = id; - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// Creates a new with the specified value. - /// - /// The unique identifier. - /// - /// is 0. - /// - public UniqueId (uint id) - { - if (id == 0) - throw new ArgumentOutOfRangeException (nameof (id)); - - this.validity = 0; - this.id = id; - } - - /// - /// Gets the identifier. - /// - /// - /// The identifier. - /// - /// The identifier. - public uint Id { - get { return id; } - } - - /// - /// Gets the validity, if non-zero. - /// - /// - /// Gets the UidValidity of the containing folder. - /// - /// The UidValidity of the containing folder. - public uint Validity { - get { return validity; } - } - - /// - /// Gets whether or not the unique identifier is valid. - /// - /// - /// Gets whether or not the unique identifier is valid. - /// - /// true if the unique identifier is valid; otherwise, false. - public bool IsValid { - get { return Id != 0; } - } - - #region IComparable implementation - - /// - /// Compares two objects. - /// - /// - /// Compares two objects. - /// - /// - /// A value less than 0 if this is less than , - /// a value of 0 if this is equal to , or - /// a value greater than 0 if this is greater than . - /// - /// The other unique identifier. - public int CompareTo (UniqueId other) - { - return Id.CompareTo (other.Id); - } - - #endregion - - #region IEquatable implementation - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public bool Equals (UniqueId other) - { - return other.Id == Id; - } - - #endregion - - /// - /// Determines whether two unique identifiers are equal. - /// - /// - /// Determines whether two unique identifiers are equal. - /// - /// true if and are equal; otherwise, false. - /// The first unique id to compare. - /// The second unique id to compare. - public static bool operator == (UniqueId uid1, UniqueId uid2) - { - return uid1.Id == uid2.Id; - } - - /// - /// Determines whether one unique identifier is greater than another unique identifier. - /// - /// - /// Determines whether one unique identifier is greater than another unique identifier. - /// - /// true if is greater than ; otherwise, false. - /// The first unique id to compare. - /// The second unique id to compare. - public static bool operator > (UniqueId uid1, UniqueId uid2) - { - return uid1.Id > uid2.Id; - } - - /// - /// Determines whether one unique identifier is greater than or equal to another unique identifier. - /// - /// - /// Determines whether one unique identifier is greater than or equal to another unique identifier. - /// - /// true if is greater than or equal to ; otherwise, false. - /// The first unique id to compare. - /// The second unique id to compare. - public static bool operator >= (UniqueId uid1, UniqueId uid2) - { - return uid1.Id >= uid2.Id; - } - - /// - /// Determines whether two unique identifiers are not equal. - /// - /// - /// Determines whether two unique identifiers are not equal. - /// - /// true if and are not equal; otherwise, false. - /// The first unique id to compare. - /// The second unique id to compare. - public static bool operator != (UniqueId uid1, UniqueId uid2) - { - return uid1.Id != uid2.Id; - } - - /// - /// Determines whether one unique identifier is less than another unique identifier. - /// - /// - /// Determines whether one unique identifier is less than another unique identifier. - /// - /// true if is less than ; otherwise, false. - /// The first unique id to compare. - /// The second unique id to compare. - public static bool operator < (UniqueId uid1, UniqueId uid2) - { - return uid1.Id < uid2.Id; - } - - /// - /// Determines whether one unique identifier is less than or equal to another unique identifier. - /// - /// - /// Determines whether one unique identifier is less than or equal to another unique identifier. - /// - /// true if is less than or equal to ; otherwise, false. - /// The first unique id to compare. - /// The second unique id to compare. - public static bool operator <= (UniqueId uid1, UniqueId uid2) - { - return uid1.Id <= uid2.Id; - } - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current ; - /// otherwise, false. - public override bool Equals (object obj) - { - return obj is UniqueId && ((UniqueId) obj).Id == Id; - } - - /// - /// Serves as a hash function for a object. - /// - /// - /// Serves as a hash function for a object. - /// - /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a hash table. - public override int GetHashCode () - { - return Id.GetHashCode (); - } - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - return Id.ToString (CultureInfo.InvariantCulture); - } - - /// - /// Attempt to parse a unique identifier. - /// - /// - /// Attempts to parse a unique identifier. - /// - /// true if the unique identifier was successfully parsed; otherwise, false.. - /// The token to parse. - /// The index to start parsing. - /// The unique identifier. - internal static bool TryParse (string token, ref int index, out uint uid) - { - uint value = 0; - - while (index < token.Length) { - char c = token[index]; - uint v; - - if (c < '0' || c > '9') - break; - - v = (uint) (c - '0'); - - if (value > uint.MaxValue / 10 || (value == uint.MaxValue / 10 && v > uint.MaxValue % 10)) { - uid = 0; - return false; - } - - value = (value * 10) + v; - index++; - } - - uid = value; - - return uid != 0; - } - - /// - /// Attempt to parse a unique identifier. - /// - /// - /// Attempts to parse a unique identifier. - /// - /// true if the unique identifier was successfully parsed; otherwise, false.. - /// The token to parse. - /// The UIDVALIDITY value. - /// The unique identifier. - /// - /// is null. - /// - public static bool TryParse (string token, uint validity, out UniqueId uid) - { - if (token == null) - throw new ArgumentNullException (nameof (token)); - - uint id; - - if (!uint.TryParse (token, NumberStyles.None, CultureInfo.InvariantCulture, out id) || id == 0) { - uid = Invalid; - return false; - } - - uid = new UniqueId (validity, id); - - return true; - } - - /// - /// Attempt to parse a unique identifier. - /// - /// - /// Attempts to parse a unique identifier. - /// - /// true if the unique identifier was successfully parsed; otherwise, false.. - /// The token to parse. - /// The unique identifier. - /// - /// is null. - /// - public static bool TryParse (string token, out UniqueId uid) - { - return TryParse (token, 0, out uid); - } - - /// - /// Parse a unique identifier. - /// - /// - /// Parses a unique identifier. - /// - /// The unique identifier. - /// A string containing the unique identifier. - /// The UIDVALIDITY. - /// - /// is null. - /// - /// - /// is not in the correct format. - /// - /// - /// The unique identifier is greater than . - /// - public static UniqueId Parse (string token, uint validity) - { - return new UniqueId (validity, uint.Parse (token, NumberStyles.None, CultureInfo.InvariantCulture)); - } - - /// - /// Parse a unique identifier. - /// - /// - /// Parses a unique identifier. - /// - /// The unique identifier. - /// A string containing the unique identifier. - /// - /// is null. - /// - /// - /// is not in the correct format. - /// - /// - /// The unique identifier is greater than . - /// - public static UniqueId Parse (string token) - { - return new UniqueId (uint.Parse (token, NumberStyles.None, CultureInfo.InvariantCulture)); - } - } -} diff --git a/src/MailKit/UniqueIdMap.cs b/src/MailKit/UniqueIdMap.cs deleted file mode 100644 index d637c33..0000000 --- a/src/MailKit/UniqueIdMap.cs +++ /dev/null @@ -1,223 +0,0 @@ -// -// UniqueIdMap.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace MailKit { - /// - /// A mapping of unique identifiers. - /// - /// - /// A can be used to discover the mapping of one set of unique identifiers - /// to another. - /// For example, when copying or moving messages from one folder to another, it is often desirable - /// to know what the unique identifiers are for each of the messages in the destination folder. - /// - public class UniqueIdMap : IReadOnlyDictionary - { - /// - /// Any empty mapping of unique identifiers. - /// - /// - /// Any empty mapping of unique identifiers. - /// - public static readonly UniqueIdMap Empty = new UniqueIdMap (); - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The unique identifiers used in the source folder. - /// The unique identifiers used in the destination folder. - /// - /// is null. - /// -or- - /// is null. - /// - public UniqueIdMap (IList source, IList destination) - { - if (source == null) - throw new ArgumentNullException (nameof (source)); - - if (destination == null) - throw new ArgumentNullException (nameof (destination)); - - Destination = destination; - Source = source; - } - - UniqueIdMap () - { - Destination = Source = new UniqueId[0]; - } - - /// - /// Gets the list of unique identifiers used in the source folder. - /// - /// - /// Gets the list of unique identifiers used in the source folder. - /// - /// The unique identifiers used in the source folder. - public IList Source { - get; private set; - } - - /// - /// Gets the list of unique identifiers used in the destination folder. - /// - /// - /// Gets the list of unique identifiers used in the destination folder. - /// - /// The unique identifiers used in the destination folder. - public IList Destination { - get; private set; - } - - /// - /// Gets the number of unique identifiers that have been remapped. - /// - /// - /// Gets the number of unique identifiers that have been remapped. - /// - /// The count. - public int Count { - get { return Source.Count; } - } - - /// - /// Gets the keys. - /// - /// - /// Gets the keys. - /// - /// The keys. - public IEnumerable Keys { - get { return Source; } - } - - /// - /// Gets the values. - /// - /// - /// Gets the values. - /// - /// The values. - public IEnumerable Values { - get { return Destination; } - } - - /// - /// Checks if the specified unique identifier has been remapped. - /// - /// - /// Checks if the specified unique identifier has been remapped. - /// - /// true if the unique identifier has been remapped; otherwise, false. - /// The unique identifier. - public bool ContainsKey (UniqueId key) - { - return Source.Contains (key); - } - - /// - /// Tries to get the remapped unique identifier. - /// - /// - /// Attempts to get the remapped unique identifier. - /// - /// true on success; otherwise, false. - /// The unique identifier of the message in the source folder. - /// The unique identifier of the message in the destination folder. - public bool TryGetValue (UniqueId key, out UniqueId value) - { - int index = Source.IndexOf (key); - - if (index == -1 || index >= Destination.Count) { - value = UniqueId.Invalid; - return false; - } - - value = Destination[index]; - - return true; - } - - /// - /// Gets the remapped unique identifier. - /// - /// - /// Gets the remapped unique identifier. - /// - /// The unique identifier of the message in the source folder. - /// - /// is out of range. - /// - public UniqueId this [UniqueId index] { - get { - UniqueId uid; - - if (!TryGetValue (index, out uid)) - throw new ArgumentOutOfRangeException (nameof (index)); - - return uid; - } - } - - /// - /// Gets the enumerator for the remapped unique identifiers. - /// - /// - /// Gets the enumerator for the remapped unique identifiers. - /// - /// The enumerator. - public IEnumerator> GetEnumerator () - { - var dst = Destination.GetEnumerator (); - var src = Source.GetEnumerator (); - - while (src.MoveNext () && dst.MoveNext ()) - yield return new KeyValuePair (src.Current, dst.Current); - - yield break; - } - - /// - /// Gets the enumerator for the remapped unique identifiers. - /// - /// - /// Gets the enumerator for the remapped unique identifiers. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - } -} diff --git a/src/MailKit/UniqueIdRange.cs b/src/MailKit/UniqueIdRange.cs deleted file mode 100644 index 244aae3..0000000 --- a/src/MailKit/UniqueIdRange.cs +++ /dev/null @@ -1,494 +0,0 @@ -// -// UniqueIdRange.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Globalization; -using System.Collections.Generic; - -namespace MailKit { - /// - /// A range of items. - /// - /// - /// When dealing with a large range, it is more efficient to use a - /// than a typical - /// IList<>. - /// - public class UniqueIdRange : IList - { - /// - /// A that encompases all messages in the folder. - /// - /// - /// Represents the range of messages from to - /// . - /// - public static readonly UniqueIdRange All = new UniqueIdRange (UniqueId.MinValue, UniqueId.MaxValue); - - static readonly UniqueIdRange Invalid = new UniqueIdRange (); - - readonly uint validity; - internal uint start; - internal uint end; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new (invalid) range of unique identifiers. - /// - UniqueIdRange () - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new range of unique identifiers. - /// - /// The uid validity. - /// The first unique identifier in the range. - /// The last unique identifier in the range. - public UniqueIdRange (uint validity, uint start, uint end) - { - if (start == 0) - throw new ArgumentOutOfRangeException (nameof (start)); - - if (end == 0) - throw new ArgumentOutOfRangeException (nameof (end)); - - this.validity = validity; - this.start = start; - this.end = end; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new range of unique identifiers. - /// - /// The first in the range. - /// The last in the range. - /// - /// is invalid. - /// -or- - /// is invalid. - /// - public UniqueIdRange (UniqueId start, UniqueId end) - { - if (!start.IsValid) - throw new ArgumentOutOfRangeException (nameof (start)); - - if (!end.IsValid) - throw new ArgumentOutOfRangeException (nameof (end)); - - this.validity = start.Validity; - this.start = start.Id; - this.end = end.Id; - } - - /// - /// Gets the validity, if non-zero. - /// - /// - /// Gets the UidValidity of the containing folder. - /// - /// The UidValidity of the containing folder. - public uint Validity { - get { return validity; } - } - - /// - /// Gets the minimum unique identifier in the range. - /// - /// - /// Gets the minimum unique identifier in the range. - /// - /// The minimum unique identifier. - public UniqueId Min { - get { return start < end ? new UniqueId (validity, start) : new UniqueId (validity, end); } - } - - /// - /// Gets the maximum unique identifier in the range. - /// - /// - /// Gets the maximum unique identifier in the range. - /// - /// The maximum unique identifier. - public UniqueId Max { - get { return start > end ? new UniqueId (validity, start) : new UniqueId (validity, end); } - } - - /// - /// Get the start of the unique identifier range. - /// - /// - /// Gets the start of the unique identifier range. - /// - /// The start of the range. - public UniqueId Start { - get { return new UniqueId (validity, start); } - } - - /// - /// Get the end of the unique identifier range. - /// - /// - /// Gets the end of the unique identifier range. - /// - /// The end of the range. - public UniqueId End { - get { return new UniqueId (validity, end); } - } - - #region ICollection implementation - - /// - /// Get the number of unique identifiers in the range. - /// - /// - /// Gets the number of unique identifiers in the range. - /// - /// The count. - public int Count { - get { return (int) (start <= end ? end - start : start - end) + 1; } - } - - /// - /// Get whether or not the range is read only. - /// - /// - /// A is always read-only. - /// - /// true if the range is read only; otherwise, false. - public bool IsReadOnly { - get { return true; } - } - - /// - /// Adds the unique identifier to the range. - /// - /// - /// Since a is read-only, unique ids cannot - /// be added to the range. - /// - /// The unique identifier to add. - /// - /// The list does not support adding items. - /// - public void Add (UniqueId uid) - { - throw new NotSupportedException (); - } - - /// - /// Clears the list. - /// - /// - /// Since a is read-only, the range cannot be cleared. - /// - /// - /// The list does not support being cleared. - /// - public void Clear () - { - throw new NotSupportedException (); - } - - /// - /// Checks if the range contains the specified unique id. - /// - /// - /// Determines whether or not the range contains the specified unique id. - /// - /// true if the specified unique identifier is in the range; otherwise false. - /// The unique id. - public bool Contains (UniqueId uid) - { - if (start <= end) - return uid.Id >= start && uid.Id <= end; - - return uid.Id <= start && uid.Id >= end; - } - - /// - /// Copies all of the unique ids in the range to the specified array. - /// - /// - /// Copies all of the unique ids within the range into the array, - /// starting at the specified array index. - /// - /// The array to copy the unique ids to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (UniqueId[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException (nameof (array)); - - if (arrayIndex < 0 || arrayIndex > (array.Length - Count)) - throw new ArgumentOutOfRangeException (nameof (arrayIndex)); - - int index = arrayIndex; - - if (start <= end) { - for (uint uid = start; uid <= end; uid++, index++) - array[index] = new UniqueId (validity, uid); - } else { - for (uint uid = start; uid >= end; uid--, index++) - array[index] = new UniqueId (validity, uid); - } - } - - /// - /// Removes the unique identifier from the range. - /// - /// - /// Since a is read-only, unique ids cannot be removed. - /// - /// true if the unique identifier was removed; otherwise false. - /// The unique identifier to remove. - /// - /// The list does not support removing items. - /// - public bool Remove (UniqueId uid) - { - throw new NotSupportedException (); - } - - #endregion - - #region IList implementation - - /// - /// Gets the index of the specified unique id, if it exists. - /// - /// - /// Finds the index of the specified unique id, if it exists. - /// - /// The index of the specified unique id; otherwise -1. - /// The unique id. - public int IndexOf (UniqueId uid) - { - if (start <= end) { - if (uid.Id < start || uid.Id > end) - return -1; - - return (int) (uid.Id - start); - } - - if (uid.Id > start || uid.Id < end) - return -1; - - return (int) (start - uid.Id); - } - - /// - /// Inserts the specified unique identifier at the given index. - /// - /// - /// Inserts the unique identifier at the specified index in the range. - /// - /// The index to insert the unique id. - /// The unique id. - /// - /// The list does not support inserting items. - /// - public void Insert (int index, UniqueId uid) - { - throw new NotSupportedException (); - } - - /// - /// Removes the unique identifier at the specified index. - /// - /// - /// Removes the unique identifier at the specified index. - /// - /// The index. - /// - /// The list does not support removing items. - /// - public void RemoveAt (int index) - { - throw new NotSupportedException (); - } - - /// - /// Gets or sets the unique identifier at the specified index. - /// - /// - /// Gets or sets the unique identifier at the specified index. - /// - /// The unique identifier at the specified index. - /// The index. - /// - /// is out of range. - /// - /// - /// The list does not support setting items. - /// - public UniqueId this [int index] { - get { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - uint uid = start <= end ? start + (uint) index : start - (uint) index; - - return new UniqueId (validity, uid); - } - set { - throw new NotSupportedException (); - } - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the range of unique ids. - /// - /// - /// Gets an enumerator for the range of unique ids. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - if (start <= end) { - for (uint uid = start; uid <= end; uid++) - yield return new UniqueId (validity, uid); - } else { - for (uint uid = start; uid >= end; uid--) - yield return new UniqueId (validity, uid); - } - - yield break; - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the range of unique ids. - /// - /// - /// Gets an enumerator for the range of unique ids. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - if (end == uint.MaxValue) - return string.Format (CultureInfo.InvariantCulture, "{0}:*", start); - - return string.Format (CultureInfo.InvariantCulture, "{0}:{1}", start, end); - } - - /// - /// Attempt to parse a unique identifier range. - /// - /// - /// Attempts to parse a unique identifier range. - /// - /// true if the unique identifier range was successfully parsed; otherwise, false.. - /// The token to parse. - /// The UIDVALIDITY value. - /// The unique identifier range. - /// - /// is null. - /// - public static bool TryParse (string token, uint validity, out UniqueIdRange range) - { - if (token == null) - throw new ArgumentNullException (nameof (token)); - - uint start, end; - int index = 0; - - if (!UniqueId.TryParse (token, ref index, out start) || index + 2 > token.Length || token[index++] != ':') { - range = Invalid; - return false; - } - - if (token[index] != '*') { - if (!UniqueId.TryParse (token, ref index, out end) || index < token.Length) { - range = Invalid; - return false; - } - } else if (index + 1 != token.Length) { - range = Invalid; - return false; - } else { - end = uint.MaxValue; - } - - range = new UniqueIdRange (validity, start, end); - - return true; - } - - /// - /// Attempt to parse a unique identifier range. - /// - /// - /// Attempts to parse a unique identifier range. - /// - /// true if the unique identifier range was successfully parsed; otherwise, false.. - /// The token to parse. - /// The unique identifier range. - /// - /// is null. - /// - public static bool TryParse (string token, out UniqueIdRange range) - { - return TryParse (token, 0, out range); - } - } -} diff --git a/src/MailKit/UniqueIdSet.cs b/src/MailKit/UniqueIdSet.cs deleted file mode 100644 index 10228b5..0000000 --- a/src/MailKit/UniqueIdSet.cs +++ /dev/null @@ -1,988 +0,0 @@ -// -// UniqueIdSet.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections; -using System.Globalization; -using System.Collections.Generic; - -using MailKit.Search; - -namespace MailKit { - /// - /// A set of unique identifiers. - /// - /// - /// When dealing with a large number of unique identifiers, it may be more efficient to use a - /// than a typical IList<>. - /// - public class UniqueIdSet : IList - { - struct Range - { - public uint Start; - public uint End; - - public Range (uint start, uint end) - { - Start = start; - End = end; - } - - public int Count { - get { return (int) (Start <= End ? End - Start : Start - End) + 1; } - } - - public bool Contains (uint uid) - { - if (Start <= End) - return uid >= Start && uid <= End; - - return uid <= Start && uid >= End; - } - - public int IndexOf (uint uid) - { - if (Start <= End) { - if (uid < Start || uid > End) - return -1; - - return (int) (uid - Start); - } - - if (uid > Start || uid < End) - return -1; - - return (int) (Start - uid); - } - - public uint this [int index] { - get { - return Start <= End ? Start + (uint) index : Start - (uint) index; - } - } - - public IEnumerator GetEnumerator () - { - if (Start <= End) { - for (uint uid = Start; uid <= End; uid++) - yield return uid; - } else { - for (uint uid = Start; uid >= End; uid--) - yield return uid; - } - - yield break; - } - - public override string ToString () - { - if (Start == End) - return Start.ToString (CultureInfo.InvariantCulture); - - if (Start <= End && End == uint.MaxValue) - return string.Format (CultureInfo.InvariantCulture, "{0}:*", Start); - - return string.Format (CultureInfo.InvariantCulture, "{0}:{1}", Start, End); - } - } - - readonly List ranges = new List (); - long count; - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new unique identifier set. - /// - /// The uid validity. - /// The sorting order to use for the unique identifiers. - /// - /// is invalid. - /// - public UniqueIdSet (uint validity, SortOrder order = SortOrder.None) - { - switch (order) { - case SortOrder.Descending: - case SortOrder.Ascending: - case SortOrder.None: - break; - default: - throw new ArgumentOutOfRangeException (nameof (order)); - } - - Validity = validity; - SortOrder = order; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new unique identifier set. - /// - /// The sorting order to use for the unique identifiers. - /// - /// is invalid. - /// - public UniqueIdSet (SortOrder order = SortOrder.None) : this (0, order) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new set of unique identifier set containing the specified uids. - /// - /// An initial set of unique ids. - /// The sorting order to use for the unique identifiers. - /// - /// is invalid. - /// - public UniqueIdSet (IEnumerable uids, SortOrder order = SortOrder.None) : this (order) - { - foreach (var uid in uids) - Add (uid); - } - - /// - /// Gets the sort order of the unique identifiers. - /// - /// - /// Gets the sort order of the unique identifiers. - /// - /// The sort order. - public SortOrder SortOrder { - get; private set; - } - - /// - /// Gets the validity, if non-zero. - /// - /// - /// Gets the UidValidity of the containing folder. - /// - /// The UidValidity of the containing folder. - public uint Validity { - get; private set; - } - - #region ICollection implementation - - /// - /// Get the number of unique ids in the set. - /// - /// - /// Gets the number of unique ids in the set. - /// - /// The count. - public int Count { - get { return (int) Math.Min (count, int.MaxValue); } - } - - /// - /// Get whether or not the set is read only. - /// - /// - /// Gets whether or not the set is read-only. - /// - /// true if the set is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - int BinarySearch (uint uid) - { - int min = 0, max = ranges.Count; - - if (max == 0) - return -1; - - do { - int i = min + ((max - min) / 2); - - if (SortOrder == SortOrder.Ascending) { - // sorted ascending: 1:3,5:7,9 - if (uid >= ranges[i].Start) { - if (uid <= ranges[i].End) - return i; - - min = i + 1; - } else { - max = i; - } - } else { - // sorted descending: 9,7:5,3:1 - if (uid >= ranges[i].End) { - if (uid <= ranges[i].Start) - return i; - - max = i; - } else { - min = i + 1; - } - } - } while (min < max); - - return -1; - } - - int IndexOfRange (uint uid) - { - if (SortOrder != SortOrder.None) - return BinarySearch (uid); - - for (int i = 0; i < ranges.Count; i++) { - if (ranges[i].Contains (uid)) - return i; - } - - return -1; - } - - void BinaryInsertAscending (uint uid) - { - int min = 0, max = ranges.Count; - int i; - - do { - i = min + ((max - min) / 2); - - if (uid >= ranges[i].Start) { - if (uid <= ranges[i].End) - return; - - if (uid == ranges[i].End + 1) { - if (i + 1 < ranges.Count && uid + 1 >= ranges[i + 1].Start) { - // merge the 2 ranges together - ranges[i] = new Range (ranges[i].Start, ranges[i + 1].End); - ranges.RemoveAt (i + 1); - count++; - return; - } - - ranges[i] = new Range (ranges[i].Start, uid); - count++; - return; - } - - min = i + 1; - i = min; - } else { - if (uid == ranges[i].Start - 1) { - if (i > 0 && uid - 1 <= ranges[i - 1].End) { - // merge the 2 ranges together - ranges[i - 1] = new Range (ranges[i - 1].Start, ranges[i].End); - ranges.RemoveAt (i); - count++; - return; - } - - ranges[i] = new Range (uid, ranges[i].End); - count++; - return; - } - - max = i; - } - } while (min < max); - - var range = new Range (uid, uid); - - if (i < ranges.Count) - ranges.Insert (i, range); - else - ranges.Add (range); - - count++; - } - - void BinaryInsertDescending (uint uid) - { - int min = 0, max = ranges.Count; - int i; - - do { - i = min + ((max - min) / 2); - - if (uid <= ranges[i].Start) { - if (uid >= ranges[i].End) - return; - - if (uid == ranges[i].End - 1) { - if (i + 1 < ranges.Count && uid - 1 <= ranges[i + 1].Start) { - // merge the 2 ranges together - ranges[i] = new Range (ranges[i].Start, ranges[i + 1].End); - ranges.RemoveAt (i + 1); - count++; - return; - } - - ranges[i] = new Range (ranges[i].Start, uid); - count++; - return; - } - - min = i + 1; - i = min; - } else { - if (uid == ranges[i].Start + 1) { - if (i > 0 && uid + 1 >= ranges[i - 1].End) { - // merge the 2 ranges together - ranges[i - 1] = new Range (ranges[i - 1].Start, ranges[i].End); - ranges.RemoveAt (i); - count++; - return; - } - - ranges[i] = new Range (uid, ranges[i].End); - count++; - return; - } - - max = i; - } - } while (min < max); - - var range = new Range (uid, uid); - - if (i < ranges.Count) - ranges.Insert (i, range); - else - ranges.Add (range); - - count++; - } - - void Append (uint uid) - { - if (IndexOfRange (uid) != -1) - return; - - count++; - - if (ranges.Count > 0) { - int index = ranges.Count - 1; - var range = ranges[index]; - - if (range.Start == range.End) { - if (uid == range.End + 1 || uid == range.End - 1) { - ranges[index] = new Range (range.Start, uid); - return; - } - } else if (range.Start < range.End) { - if (uid == range.End + 1) { - ranges[index] = new Range (range.Start, uid); - return; - } - } else if (range.Start > range.End) { - if (uid == range.End - 1) { - ranges[index] = new Range (range.Start, uid); - return; - } - } - } - - ranges.Add (new Range (uid, uid)); - } - - /// - /// Adds the unique identifier to the set. - /// - /// - /// Adds the unique identifier to the set. - /// - /// The unique identifier to add. - /// - /// is invalid. - /// - public void Add (UniqueId uid) - { - if (!uid.IsValid) - throw new ArgumentException ("Invalid unique identifier.", nameof (uid)); - - if (ranges.Count == 0) { - ranges.Add (new Range (uid.Id, uid.Id)); - count++; - return; - } - - switch (SortOrder) { - case SortOrder.Descending: - BinaryInsertDescending (uid.Id); - break; - case SortOrder.Ascending: - BinaryInsertAscending (uid.Id); - break; - default: - Append (uid.Id); - break; - } - } - - /// - /// Adds all of the uids to the set. - /// - /// - /// Adds all of the uids to the set. - /// - /// The collection of uids. - public void AddRange (IEnumerable uids) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - foreach (var uid in uids) - Add (uid); - } - - /// - /// Clears the list. - /// - /// - /// Clears the list. - /// - /// - /// The collection is readonly. - /// - public void Clear () - { - ranges.Clear (); - count = 0; - } - - /// - /// Checks if the set contains the specified unique id. - /// - /// - /// Determines whether or not the set contains the specified unique id. - /// - /// true if the specified unique identifier is in the set; otherwise false. - /// The unique id. - public bool Contains (UniqueId uid) - { - return IndexOfRange (uid.Id) != -1; - } - - /// - /// Copies all of the unique ids in the set to the specified array. - /// - /// - /// Copies all of the unique ids within the set into the array, - /// starting at the specified array index. - /// - /// The array to copy the unique ids to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of set. - /// - public void CopyTo (UniqueId[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException (nameof (array)); - - if (arrayIndex < 0 || arrayIndex > (array.Length - Count)) - throw new ArgumentOutOfRangeException (nameof (arrayIndex)); - - int index = arrayIndex; - - for (int i = 0; i < ranges.Count; i++) { - foreach (var uid in ranges[i]) - array[index++] = new UniqueId (Validity, uid); - } - } - - void Remove (int index, uint uid) - { - var range = ranges[index]; - - if (uid == range.Start) { - // remove the first item in the range - if (range.Start != range.End) { - if (range.Start <= range.End) - ranges[index] = new Range (uid + 1, range.End); - else - ranges[index] = new Range (uid - 1, range.End); - } else { - ranges.RemoveAt (index); - } - } else if (uid == range.End) { - // remove the last item in the range - if (range.Start <= range.End) - ranges[index] = new Range (range.Start, uid - 1); - else - ranges[index] = new Range (range.Start, uid + 1); - } else { - // remove a uid from the middle of the range - if (range.Start < range.End) { - ranges.Insert (index, new Range (range.Start, uid - 1)); - ranges[index + 1] = new Range (uid + 1, range.End); - } else { - ranges.Insert (index, new Range (range.Start, uid + 1)); - ranges[index + 1] = new Range (uid - 1, range.End); - } - } - - count--; - } - - /// - /// Removes the unique identifier from the set. - /// - /// - /// Removes the unique identifier from the set. - /// - /// true if the unique identifier was removed; otherwise false. - /// The unique identifier to remove. - public bool Remove (UniqueId uid) - { - int index = IndexOfRange (uid.Id); - - if (index == -1) - return false; - - Remove (index, uid.Id); - - return true; - } - - #endregion - - #region IList implementation - - /// - /// Gets the index of the specified unique id, if it exists. - /// - /// - /// Finds the index of the specified unique id, if it exists. - /// - /// The index of the specified unique id; otherwise -1. - /// The unique id. - public int IndexOf (UniqueId uid) - { - int index = 0; - - for (int i = 0; i < ranges.Count; i++) { - if (ranges[i].Contains (uid.Id)) - return index + ranges[i].IndexOf (uid.Id); - - index += ranges[i].Count; - } - - return -1; - } - - /// - /// Inserts the specified unique identifier at the given index. - /// - /// - /// Inserts the unique identifier at the specified index in the set. - /// - /// The index to insert the unique id. - /// The unique id. - /// - /// The list does not support inserting items. - /// - public void Insert (int index, UniqueId uid) - { - throw new NotSupportedException (); - } - - /// - /// Removes the unique identifier at the specified index. - /// - /// - /// Removes the unique identifier at the specified index. - /// - /// The index. - /// - /// is out of range. - /// - public void RemoveAt (int index) - { - if (index < 0 || index >= count) - throw new ArgumentOutOfRangeException (nameof (index)); - - int offset = 0; - - for (int i = 0; i < ranges.Count; i++) { - if (index >= offset + ranges[i].Count) { - offset += ranges[i].Count; - continue; - } - - var uid = ranges[i][index - offset]; - Remove (i, uid); - return; - } - } - - /// - /// Gets or sets the unique identifier at the specified index. - /// - /// - /// Gets or sets the unique identifier at the specified index. - /// - /// The unique identifier at the specified index. - /// The index. - /// - /// is out of range. - /// - /// - /// The list does not support setting items. - /// - public UniqueId this [int index] { - get { - if (index < 0 || index >= count) - throw new ArgumentOutOfRangeException (nameof (index)); - - int offset = 0; - - for (int i = 0; i < ranges.Count; i++) { - if (index >= offset + ranges[i].Count) { - offset += ranges[i].Count; - continue; - } - - uint uid = ranges[i][index - offset]; - - return new UniqueId (Validity, uid); - } - - throw new ArgumentOutOfRangeException (nameof (index)); - } - set { - throw new NotSupportedException (); - } - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the set of unique ids. - /// - /// - /// Gets an enumerator for the set of unique ids. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - for (int i = 0; i < ranges.Count; i++) { - foreach (var uid in ranges[i]) - yield return new UniqueId (Validity, uid); - } - - yield break; - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the set of unique ids. - /// - /// - /// Gets an enumerator for the set of unique ids. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - foreach (var subset in EnumerateSerializedSubsets (int.MaxValue)) - return subset; - - return string.Empty; - } - - /// - /// Format a generic list of unique identifiers as a string. - /// - /// - /// Formats a generic list of unique identifiers as a string. - /// - /// The string representation of the collection of unique identifiers. - /// The unique identifiers. - /// - /// is null. - /// - /// - /// One or more of the unique identifiers is invalid (has a value of 0). - /// - public static string ToString (IList uids) - { - foreach (var subset in EnumerateSerializedSubsets (uids, int.MaxValue)) - return subset; - - return string.Empty; - } - - /// - /// Format the set of unique identifiers as multiple strings that fit within the maximum defined character length. - /// - /// - /// Formats the set of unique identifiers as multiple strings that fit within the maximum defined character length. - /// - /// A list of strings representing the collection of unique identifiers. - /// The maximum length of any returned string of UIDs. - /// - /// is negative. - /// - IEnumerable EnumerateSerializedSubsets (int maxLength) - { - if (maxLength < 0) - throw new ArgumentOutOfRangeException (nameof (maxLength)); - - var builder = new StringBuilder (); - - for (int i = 0; i < ranges.Count; i++) { - var range = ranges[i].ToString (); - - if (builder.Length > 0) { - if (builder.Length + 1 + range.Length > maxLength) { - yield return builder.ToString (); - builder.Clear (); - } else { - builder.Append (','); - } - } - - builder.Append (range); - } - - yield return builder.ToString (); - } - - /// - /// Format a generic list of unique identifiers as multiple strings that fit within the maximum defined character length. - /// - /// - /// Formats a generic list of unique identifiers as multiple strings that fit within the maximum defined character length. - /// - /// A list of strings representing the collection of unique identifiers. - /// The unique identifiers. - /// The maximum length of any returned string of UIDs. - /// - /// is null. - /// - /// - /// One or more of the unique identifiers is invalid (has a value of 0). - /// - /// - /// is negative. - /// - internal static IEnumerable EnumerateSerializedSubsets (IList uids, int maxLength) - { - if (uids == null) - throw new ArgumentNullException (nameof (uids)); - - if (maxLength < 0) - throw new ArgumentOutOfRangeException (nameof (maxLength)); - - if (uids.Count == 0) { - yield return string.Empty; - yield break; - } - - var range = uids as UniqueIdRange; - if (range != null) { - yield return range.ToString (); - yield break; - } - - var set = uids as UniqueIdSet; - if (set != null) { - foreach (var subset in set.EnumerateSerializedSubsets (maxLength)) - yield return subset; - yield break; - } - - var builder = new StringBuilder (); - int index = 0; - - while (index < uids.Count) { - if (!uids[index].IsValid) - throw new ArgumentException ("One or more of the uids is invalid.", nameof (uids)); - - uint start = uids[index].Id; - uint end = uids[index].Id; - int i = index + 1; - - if (i < uids.Count) { - if (uids[i].Id == end + 1) { - end = uids[i++].Id; - - while (i < uids.Count && uids[i].Id == end + 1) { - end++; - i++; - } - } else if (uids[i].Id == end - 1) { - end = uids[i++].Id; - - while (i < uids.Count && uids[i].Id == end - 1) { - end--; - i++; - } - } - } - - string next; - if (start != end) - next = string.Format (CultureInfo.InvariantCulture, "{0}:{1}", start, end); - else - next = start.ToString (); - - if (builder.Length > 0) { - if (builder.Length + 1 + next.Length > maxLength) { - yield return builder.ToString (); - builder.Clear (); - } else { - builder.Append (','); - } - } - - builder.Append (next); - index = i; - } - - yield return builder.ToString (); - } - - /// - /// Attempt to parse the specified token as a set of unique identifiers. - /// - /// - /// Attempts to parse the specified token as a set of unique identifiers. - /// - /// true if the set of unique identifiers were successfully parsed; otherwise, false. - /// The token containing the set of unique identifiers. - /// The UIDVALIDITY value. - /// The set of unique identifiers. - /// - /// is null. - /// - public static bool TryParse (string token, uint validity, out UniqueIdSet uids) - { - if (token == null) - throw new ArgumentNullException (nameof (token)); - - uids = new UniqueIdSet (validity); - - var order = SortOrder.None; - bool sorted = true; - uint start, end; - uint prev = 0; - int index = 0; - - do { - if (!UniqueId.TryParse (token, ref index, out start)) - return false; - - if (index < token.Length && token[index] == ':') { - index++; - - if (!UniqueId.TryParse (token, ref index, out end)) - return false; - - var range = new Range (start, end); - uids.count += range.Count; - uids.ranges.Add (range); - - if (sorted) { - switch (order) { - default: sorted = true; order = start <= end ? SortOrder.Ascending : SortOrder.Descending; break; - case SortOrder.Descending: sorted = start >= end && start <= prev; break; - case SortOrder.Ascending: sorted = start <= end && start >= prev; break; - } - } - - prev = end; - } else { - uids.ranges.Add (new Range (start, start)); - uids.count++; - - if (sorted && uids.ranges.Count > 1) { - switch (order) { - default: sorted = true; order = start >= prev ? SortOrder.Ascending : SortOrder.Descending; break; - case SortOrder.Descending: sorted = start <= prev; break; - case SortOrder.Ascending: sorted = start >= prev; break; - } - } - - prev = start; - } - - if (index >= token.Length) - break; - - if (token[index++] != ',') - return false; - } while (true); - - uids.SortOrder = sorted ? order : SortOrder.None; - - return true; - } - - /// - /// Attempt to parse the specified token as a set of unique identifiers. - /// - /// - /// Attempts to parse the specified token as a set of unique identifiers. - /// - /// true if the set of unique identifiers were successfully parsed; otherwise, false. - /// The token containing the set of unique identifiers. - /// The set of unique identifiers. - /// - /// is null. - /// - public static bool TryParse (string token, out UniqueIdSet uids) - { - return TryParse (token, 0, out uids); - } - } -} diff --git a/src/MailKit/UriExtensions.cs b/src/MailKit/UriExtensions.cs deleted file mode 100644 index 326ae2f..0000000 --- a/src/MailKit/UriExtensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -// -// UriExtensions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -namespace MailKit { - static class UriExtensions - { - public static IDictionary ParsedQuery (this Uri uri) - { - var properties = new Dictionary (StringComparer.OrdinalIgnoreCase); - int index = 1; - - if (string.IsNullOrEmpty (uri.Query)) - return properties; - - // Note: the query string begins with '?' - while (index < uri.Query.Length) { - int startIndex = index; - - while (index < uri.Query.Length && uri.Query[index] != '=') - index++; - - var name = uri.Query.Substring (startIndex, index - startIndex); - - if (index >= uri.Query.Length) { - properties.Add (name, string.Empty); - break; - } - - startIndex = ++index; - - while (index < uri.Query.Length && uri.Query[index] != '&') - index++; - - var value = uri.Query.Substring (startIndex, index - startIndex); - - properties.Add (name, Uri.UnescapeDataString (value)); - - index++; - } - - return properties; - } - } -} diff --git a/src/MailKit/mailkit.snk b/src/MailKit/mailkit.snk deleted file mode 100644 index 31cbc70..0000000 Binary files a/src/MailKit/mailkit.snk and /dev/null differ diff --git a/src/MailKitMailModule.cs b/src/MailKitMailModule.cs index 767d184..06c9f29 100644 --- a/src/MailKitMailModule.cs +++ b/src/MailKitMailModule.cs @@ -1,4 +1,5 @@ -using MimeKit; +using MailKit.Net.Smtp; +using MimeKit; using Mono.Addins; using Nini.Config; using OpenMetaverse; @@ -105,11 +106,33 @@ namespace OpenSim.Modules.EMail public void SendEmail(UUID objectID, string address, string subject, string body) { + SceneObjectPart sceneObject = m_scene.GetSceneObjectPart(objectID); + + MimeMessage message = new MimeMessage(); + message.From.Add(new MailboxAddress(sceneObject.Name, sceneObject.UUID + "@" + SMTP_SERVER_SENDER)); + message.To.Add(new MailboxAddress("", address)); + message.Subject = subject; + message.Body = new TextPart("plain"){Text = body}; + + message.Headers.Add(new Header(Encoding.UTF8, "ObjectID", sceneObject.UUID.ToString())); + message.Headers.Add(new Header(Encoding.UTF8, "AvatarID", sceneObject.OwnerID.ToString())); + message.Headers.Add(new Header(Encoding.UTF8, "Location", m_scene.Name + "@" + sceneObject.GetWorldPosition().ToString())); + + using (var client = new SmtpClient()) + { + client.Connect(SMTP_SERVER_HOSTNAME, SMTP_SERVER_PORT, SMTP_SERVER_ENCRYPTION); + + client.Authenticate(SMTP_SERVER_LOGIN, SMTP_SERVER_PASSWORD); + + client.Send(message); + client.Disconnect(true); + } } public Email GetNextEmail(UUID objectID, string sender, string subject) { + return null; } } } diff --git a/src/MimeKit/AsyncMimeParser.cs b/src/MimeKit/AsyncMimeParser.cs deleted file mode 100644 index 3471a50..0000000 --- a/src/MimeKit/AsyncMimeParser.cs +++ /dev/null @@ -1,706 +0,0 @@ -// -// AsyncMimeParser.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Diagnostics; -using System.Threading.Tasks; - -using MimeKit.IO; -using MimeKit.Utils; - -namespace MimeKit { - public partial class MimeParser - { - async Task ReadAheadAsync (int atleast, int save, CancellationToken cancellationToken) - { - int left, start, end; - - if (!AlignReadAheadBuffer (atleast, save, out left, out start, out end)) - return left; - - int nread = await stream.ReadAsync (input, start, end - start, cancellationToken).ConfigureAwait (false); - - if (nread > 0) { - inputEnd += nread; - position += nread; - } else { - eos = true; - } - - return inputEnd - inputIndex; - } - - async Task StepByteOrderMarkAsync (CancellationToken cancellationToken) - { - int bomIndex = 0; - - do { - var available = await ReadAheadAsync (ReadAheadSize, 0, cancellationToken).ConfigureAwait (false); - - if (available <= 0) { - // failed to read any data... EOF - inputIndex = inputEnd; - return false; - } - - unsafe { - fixed (byte* inbuf = input) { - StepByteOrderMark (inbuf, ref bomIndex); - } - } - } while (inputIndex == inputEnd); - - return bomIndex == 0 || bomIndex == UTF8ByteOrderMark.Length; - } - - async Task StepMboxMarkerAsync (CancellationToken cancellationToken) - { - bool complete = false; - bool needInput; - int left = 0; - - mboxMarkerLength = 0; - - do { - var available = await ReadAheadAsync (Math.Max (ReadAheadSize, left), 0, cancellationToken).ConfigureAwait (false); - - if (available <= left) { - // failed to find a From line; EOF reached - state = MimeParserState.Error; - inputIndex = inputEnd; - return; - } - - needInput = false; - - unsafe { - fixed (byte* inbuf = input) { - StepMboxMarker (inbuf, ref needInput, ref complete, ref left); - } - } - } while (!complete); - - state = MimeParserState.MessageHeaders; - } - - async Task StepHeadersAsync (CancellationToken cancellationToken) - { - bool scanningFieldName = true; - bool checkFolded = false; - bool midline = false; - bool blank = false; - bool valid = true; - int left = 0; - - headerBlockBegin = GetOffset (inputIndex); - boundary = BoundaryType.None; - ResetRawHeaderData (); - headers.Clear (); - - await ReadAheadAsync (Math.Max (ReadAheadSize, left), 0, cancellationToken).ConfigureAwait (false); - - do { - unsafe { - fixed (byte *inbuf = input) { - if (!StepHeaders (inbuf, ref scanningFieldName, ref checkFolded, ref midline, ref blank, ref valid, ref left)) - break; - } - } - - var available = await ReadAheadAsync (left + 1, 0, cancellationToken).ConfigureAwait (false); - - if (available == left) { - // EOF reached before we reached the end of the headers... - if (scanningFieldName && left > 0) { - // EOF reached right in the middle of a header field name. Throw an error. - // - // See private email from Feb 8, 2018 which contained a sample message w/o - // any breaks between the header and message body. The file also did not - // end with a newline sequence. - state = MimeParserState.Error; - } else { - // EOF reached somewhere in the middle of the value. - // - // Append whatever data we've got left and pretend we found the end - // of the header value (and the header block). - // - // For more details, see https://github.com/jstedfast/MimeKit/pull/51 - // and https://github.com/jstedfast/MimeKit/issues/348 - if (left > 0) { - AppendRawHeaderData (inputIndex, left); - inputIndex = inputEnd; - } - - ParseAndAppendHeader (); - - state = MimeParserState.Content; - } - break; - } - } while (true); - - headerBlockEnd = GetOffset (inputIndex); - } - - async Task SkipLineAsync (bool consumeNewLine, CancellationToken cancellationToken) - { - do { - unsafe { - fixed (byte* inbuf = input) { - if (SkipLine (inbuf, consumeNewLine)) - return true; - } - } - - if (await ReadAheadAsync (ReadAheadSize, 1, cancellationToken).ConfigureAwait (false) <= 0) - return false; - } while (true); - } - - async Task StepAsync (CancellationToken cancellationToken) - { - switch (state) { - case MimeParserState.Initialized: - if (!await StepByteOrderMarkAsync (cancellationToken).ConfigureAwait (false)) { - state = MimeParserState.Eos; - break; - } - - state = format == MimeFormat.Mbox ? MimeParserState.MboxMarker : MimeParserState.MessageHeaders; - break; - case MimeParserState.MboxMarker: - await StepMboxMarkerAsync (cancellationToken).ConfigureAwait (false); - break; - case MimeParserState.MessageHeaders: - case MimeParserState.Headers: - await StepHeadersAsync (cancellationToken).ConfigureAwait (false); - toplevel = false; - break; - } - - return state; - } - - async Task ScanContentAsync (Stream content, bool trimNewLine, CancellationToken cancellationToken) - { - int atleast = Math.Max (ReadAheadSize, GetMaxBoundaryLength ()); - int contentIndex = inputIndex; - var formats = new bool[2]; - bool midline = false; - int nleft; - - do { - if (contentIndex < inputIndex) - content.Write (input, contentIndex, inputIndex - contentIndex); - - nleft = inputEnd - inputIndex; - if (await ReadAheadAsync (atleast, 2, cancellationToken).ConfigureAwait (false) <= 0) { - boundary = BoundaryType.Eos; - contentIndex = inputIndex; - break; - } - - unsafe { - fixed (byte* inbuf = input) { - ScanContent (inbuf, ref contentIndex, ref nleft, ref midline, ref formats); - } - } - } while (boundary == BoundaryType.None); - - if (contentIndex < inputIndex) - content.Write (input, contentIndex, inputIndex - contentIndex); - - var isEmpty = content.Length == 0; - - if (boundary != BoundaryType.Eos && trimNewLine) { - // the last \r\n belongs to the boundary - if (content.Length > 0) { - if (input[inputIndex - 2] == (byte) '\r') - content.SetLength (content.Length - 2); - else - content.SetLength (content.Length - 1); - } - } - - return new ScanContentResult (formats, isEmpty); - } - - async Task ConstructMimePartAsync (MimePart part, CancellationToken cancellationToken) - { - long endOffset, beginOffset = GetOffset (inputIndex); - var beginLineNumber = lineNumber; - ScanContentResult result; - Stream content; - - OnMimeContentBegin (part, beginOffset); - - if (persistent) { - using (var measured = new MeasuringStream ()) { - result = await ScanContentAsync (measured, true, cancellationToken).ConfigureAwait (false); - endOffset = beginOffset + measured.Length; - } - - content = new BoundStream (stream, beginOffset, endOffset, true); - } else { - content = new MemoryBlockStream (); - result = await ScanContentAsync (content, true, cancellationToken).ConfigureAwait (false); - content.Seek (0, SeekOrigin.Begin); - endOffset = beginOffset + content.Length; - } - - OnMimeContentEnd (part, endOffset); - OnMimeContentOctets (part, endOffset - beginOffset); - OnMimeContentLines (part, lineNumber - beginLineNumber); - - if (!result.IsEmpty) - part.Content = new MimeContent (content, part.ContentTransferEncoding) { NewLineFormat = result.Format }; - else - content.Dispose (); - } - - async Task ConstructMessagePartAsync (MessagePart rfc822, int depth, CancellationToken cancellationToken) - { - var beginOffset = GetOffset (inputIndex); - var beginLineNumber = lineNumber; - - OnMimeContentBegin (rfc822, beginOffset); - - if (bounds.Count > 0) { - int atleast = Math.Max (ReadAheadSize, GetMaxBoundaryLength ()); - - if (await ReadAheadAsync (atleast, 0, cancellationToken).ConfigureAwait (false) <= 0) { - boundary = BoundaryType.Eos; - return; - } - - unsafe { - fixed (byte* inbuf = input) { - byte* start = inbuf + inputIndex; - byte* inend = inbuf + inputEnd; - byte* inptr = start; - - *inend = (byte) '\n'; - - while (*inptr != (byte) '\n') - inptr++; - - boundary = CheckBoundary (inputIndex, start, (int) (inptr - start)); - - switch (boundary) { - case BoundaryType.ImmediateEndBoundary: - case BoundaryType.ImmediateBoundary: - case BoundaryType.ParentBoundary: - return; - case BoundaryType.ParentEndBoundary: - // ignore "From " boundaries, broken mailers tend to include these... - if (!IsMboxMarker (start)) - return; - break; - } - } - } - } - - // parse the headers... - state = MimeParserState.MessageHeaders; - if (await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) { - // Note: this either means that StepHeaders() found the end of the stream - // or an invalid header field name at the start of the message headers, - // which likely means that this is not a valid MIME stream? - boundary = BoundaryType.Eos; - return; - } - - var message = new MimeMessage (options, headers, RfcComplianceMode.Loose); - var type = GetContentType (null); - - if (preHeaderBuffer.Length > 0) { - message.MboxMarker = new byte[preHeaderLength]; - Buffer.BlockCopy (preHeaderBuffer, 0, message.MboxMarker, 0, preHeaderLength); - } - - var entity = options.CreateEntity (type, headers, true, depth); - message.Body = entity; - - OnMimeMessageBegin (message, headerBlockBegin); - OnMimeMessageHeadersEnd (message, headerBlockEnd); - OnMimeEntityBegin (entity, headerBlockBegin); - OnMimeEntityHeadersEnd (entity, headerBlockEnd); - - if (entity is Multipart) - await ConstructMultipartAsync ((Multipart) entity, depth + 1, cancellationToken).ConfigureAwait (false); - else if (entity is MessagePart) - await ConstructMessagePartAsync ((MessagePart) entity, depth + 1, cancellationToken).ConfigureAwait (false); - else - await ConstructMimePartAsync ((MimePart) entity, cancellationToken).ConfigureAwait (false); - - rfc822.Message = message; - - var endOffset = GetOffset (inputIndex); - OnMimeEntityEnd (entity, endOffset); - OnMimeMessageEnd (message, endOffset); - OnMimeContentEnd (rfc822, endOffset); - OnMimeContentOctets (rfc822, endOffset - beginOffset); - OnMimeContentLines (rfc822, lineNumber - beginLineNumber); - } - - async Task MultipartScanPreambleAsync (Multipart multipart, CancellationToken cancellationToken) - { - using (var memory = new MemoryStream ()) { - long offset = GetOffset (inputIndex); - - OnMultipartPreambleBegin (multipart, offset); - await ScanContentAsync (memory, false, cancellationToken).ConfigureAwait (false); - multipart.RawPreamble = memory.ToArray (); - OnMultipartPreambleEnd (multipart, offset + memory.Length); - } - } - - async Task MultipartScanEpilogueAsync (Multipart multipart, CancellationToken cancellationToken) - { - using (var memory = new MemoryStream ()) { - long offset = GetOffset (inputIndex); - - OnMultipartEpilogueBegin (multipart, offset); - var result = await ScanContentAsync (memory, true, cancellationToken).ConfigureAwait (false); - multipart.RawEpilogue = result.IsEmpty ? null : memory.ToArray (); - OnMultipartEpilogueEnd (multipart, offset + memory.Length); - } - } - - async Task MultipartScanSubpartsAsync (Multipart multipart, int depth, CancellationToken cancellationToken) - { - do { - OnMultipartBoundaryBegin (multipart, GetOffset (inputIndex)); - - // skip over the boundary marker - if (!await SkipLineAsync (true, cancellationToken).ConfigureAwait (false)) { - OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); - boundary = BoundaryType.Eos; - return; - } - - OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); - - // parse the headers - state = MimeParserState.Headers; - if (await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) { - boundary = BoundaryType.Eos; - return; - } - - if (state == MimeParserState.Boundary) { - if (headers.Count == 0) { - if (boundary == BoundaryType.ImmediateBoundary) - continue; - break; - } - - // This part has no content, but that will be handled in ConstructMultipartAsync() - // or ConstructMimePartAsync(). - } - - //if (state == ParserState.Complete && headers.Count == 0) - // return BoundaryType.EndBoundary; - - var type = GetContentType (multipart.ContentType); - var entity = options.CreateEntity (type, headers, false, depth); - - OnMimeEntityBegin (entity, headerBlockBegin); - OnMimeEntityHeadersEnd (entity, headerBlockEnd); - - if (entity is Multipart) - await ConstructMultipartAsync ((Multipart) entity, depth + 1, cancellationToken).ConfigureAwait (false); - else if (entity is MessagePart) - await ConstructMessagePartAsync ((MessagePart) entity, depth + 1, cancellationToken).ConfigureAwait (false); - else - await ConstructMimePartAsync ((MimePart) entity, cancellationToken).ConfigureAwait (false); - - OnMimeEntityEnd (entity, GetOffset (inputIndex)); - - multipart.Add (entity); - } while (boundary == BoundaryType.ImmediateBoundary); - } - - async Task ConstructMultipartAsync (Multipart multipart, int depth, CancellationToken cancellationToken) - { - var beginOffset = GetOffset (inputIndex); - var beginLineNumber = lineNumber; - var marker = multipart.Boundary; - long endOffset; - - OnMimeContentBegin (multipart, beginOffset); - - if (marker == null) { -#if DEBUG - Debug.WriteLine ("Multipart without a boundary encountered!"); -#endif - - // Note: this will scan all content into the preamble... - await MultipartScanPreambleAsync (multipart, cancellationToken).ConfigureAwait (false); - endOffset = GetOffset (inputIndex); - - OnMimeContentEnd (multipart, endOffset); - OnMimeContentOctets (multipart, endOffset - beginOffset); - OnMimeContentLines (multipart, lineNumber - beginLineNumber); - return; - } - - PushBoundary (marker); - - await MultipartScanPreambleAsync (multipart, cancellationToken).ConfigureAwait (false); - if (boundary == BoundaryType.ImmediateBoundary) - await MultipartScanSubpartsAsync (multipart, depth, cancellationToken).ConfigureAwait (false); - - if (boundary == BoundaryType.ImmediateEndBoundary) { - OnMultipartEndBoundaryBegin (multipart, GetOffset (inputIndex)); - - // consume the end boundary and read the epilogue (if there is one) - multipart.WriteEndBoundary = true; - await SkipLineAsync (false, cancellationToken).ConfigureAwait (false); - PopBoundary (); - - OnMultipartEndBoundaryEnd (multipart, GetOffset (inputIndex)); - - await MultipartScanEpilogueAsync (multipart, cancellationToken).ConfigureAwait (false); - endOffset = GetOffset (inputIndex); - - OnMimeContentEnd (multipart, endOffset); - OnMimeContentOctets (multipart, endOffset - beginOffset); - OnMimeContentLines (multipart, lineNumber - beginLineNumber); - return; - } - - endOffset = GetOffset (inputIndex); - - OnMimeContentEnd (multipart, endOffset); - OnMimeContentOctets (multipart, endOffset - beginOffset); - OnMimeContentLines (multipart, lineNumber - beginLineNumber); - - multipart.WriteEndBoundary = false; - - // We either found the end of the stream or we found a parent's boundary - PopBoundary (); - - unsafe { - fixed (byte* inbuf = input) { - if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true)) - boundary = BoundaryType.ImmediateEndBoundary; - else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false)) - boundary = BoundaryType.ImmediateBoundary; - } - } - } - - /// - /// Asynchronously parses a list of headers from the stream. - /// - /// - /// Parses a list of headers from the stream. - /// - /// The parsed list of headers. - /// The cancellation token. - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the headers. - /// - /// - /// An I/O error occurred. - /// - public async Task ParseHeadersAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - state = MimeParserState.Headers; - if (await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) - throw new FormatException ("Failed to parse headers."); - - state = eos ? MimeParserState.Eos : MimeParserState.Complete; - - var parsed = new HeaderList (options); - foreach (var header in headers) - parsed.Add (header); - - return parsed; - } - - /// - /// Asynchronously parses an entity from the stream. - /// - /// - /// Parses an entity from the stream. - /// - /// The parsed entity. - /// The cancellation token. - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public async Task ParseEntityAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - // Note: if a previously parsed MimePart's content has been read, - // then the stream position will have moved and will need to be - // reset. - if (persistent && stream.Position != position) - stream.Seek (position, SeekOrigin.Begin); - - state = MimeParserState.Headers; - toplevel = true; - - if (await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) - throw new FormatException ("Failed to parse entity headers."); - - var type = GetContentType (null); - - // Note: we pass 'false' as the 'toplevel' argument here because - // we want the entity to consume all of the headers. - var entity = options.CreateEntity (type, headers, false, 0); - - OnMimeEntityBegin (entity, headerBlockBegin); - OnMimeEntityHeadersEnd (entity, headerBlockEnd); - - if (entity is Multipart) - await ConstructMultipartAsync ((Multipart) entity, 0, cancellationToken).ConfigureAwait (false); - else if (entity is MessagePart) - await ConstructMessagePartAsync ((MessagePart) entity, 0, cancellationToken).ConfigureAwait (false); - else - await ConstructMimePartAsync ((MimePart) entity, cancellationToken).ConfigureAwait (false); - - OnMimeEntityEnd (entity, GetOffset (inputIndex)); - - if (boundary != BoundaryType.Eos) - state = MimeParserState.Complete; - else - state = MimeParserState.Eos; - - return entity; - } - - /// - /// Asynchronously parses a message from the stream. - /// - /// - /// Parses a message from the stream. - /// - /// The parsed message. - /// The cancellation token. - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the message. - /// - /// - /// An I/O error occurred. - /// - public async Task ParseMessageAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - // Note: if a previously parsed MimePart's content has been read, - // then the stream position will have moved and will need to be - // reset. - if (persistent && stream.Position != position) - stream.Seek (position, SeekOrigin.Begin); - - // scan the from-line if we are parsing an mbox - while (state != MimeParserState.MessageHeaders) { - switch (await StepAsync (cancellationToken).ConfigureAwait (false)) { - case MimeParserState.Error: - throw new FormatException ("Failed to find mbox From marker."); - case MimeParserState.Eos: - throw new FormatException ("End of stream."); - } - } - - toplevel = true; - - // parse the headers - if (state < MimeParserState.Content && await StepAsync (cancellationToken).ConfigureAwait (false) == MimeParserState.Error) - throw new FormatException ("Failed to parse message headers."); - - var message = new MimeMessage (options, headers, RfcComplianceMode.Loose); - - OnMimeMessageBegin (message, headerBlockBegin); - OnMimeMessageHeadersEnd (message, headerBlockEnd); - - if (format == MimeFormat.Mbox && options.RespectContentLength) { - contentEnd = 0; - - for (int i = 0; i < headers.Count; i++) { - if (headers[i].Id != HeaderId.ContentLength) - continue; - - var value = headers[i].RawValue; - int length, index = 0; - - if (!ParseUtils.SkipWhiteSpace (value, ref index, value.Length)) - continue; - - if (!ParseUtils.TryParseInt32 (value, ref index, value.Length, out length)) - continue; - - contentEnd = GetOffset (inputIndex) + length; - break; - } - } - - var type = GetContentType (null); - var entity = options.CreateEntity (type, headers, true, 0); - message.Body = entity; - - OnMimeEntityBegin (entity, headerBlockBegin); - OnMimeEntityHeadersEnd (entity, headerBlockEnd); - - if (entity is Multipart) - await ConstructMultipartAsync ((Multipart) entity, 0, cancellationToken).ConfigureAwait (false); - else if (entity is MessagePart) - await ConstructMessagePartAsync ((MessagePart) entity, 0, cancellationToken).ConfigureAwait (false); - else - await ConstructMimePartAsync ((MimePart) entity, cancellationToken).ConfigureAwait (false); - - var endOffset = GetOffset (inputIndex); - OnMimeEntityEnd (entity, endOffset); - OnMimeMessageEnd (message, endOffset); - - if (boundary != BoundaryType.Eos) { - if (format == MimeFormat.Mbox) - state = MimeParserState.MboxMarker; - else - state = MimeParserState.Complete; - } else { - state = MimeParserState.Eos; - } - - return message; - } - } -} diff --git a/src/MimeKit/AttachmentCollection.cs b/src/MimeKit/AttachmentCollection.cs deleted file mode 100644 index d6cea7f..0000000 --- a/src/MimeKit/AttachmentCollection.cs +++ /dev/null @@ -1,653 +0,0 @@ -// -// AttachmentCollection.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections; -using System.Collections.Generic; - -using MimeKit.IO; -using MimeKit.IO.Filters; - -namespace MimeKit { - /// - /// A collection of attachments. - /// - /// - /// The is only used when building a message body with a . - /// - /// - /// - /// - public class AttachmentCollection : IList - { - readonly List attachments; - readonly bool linked; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// If is true, then the attachments - /// are treated as if they are linked to another . - /// - /// If set to true; the attachments are treated as linked resources. - public AttachmentCollection (bool linkedResources) - { - attachments = new List (); - linked = linkedResources; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public AttachmentCollection () : this (false) - { - } - - #region IList implementation - - /// - /// Gets the number of attachments currently in the collection. - /// - /// - /// Indicates the number of attachments in the collection. - /// - /// The number of attachments. - public int Count { - get { return attachments.Count; } - } - - /// - /// Gets whther or not the collection is read-only. - /// - /// - /// A is never read-only. - /// - /// true if the collection is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Gets or sets the at the specified index. - /// - /// - /// Gets or sets the at the specified index. - /// - /// The attachment at the specified index. - /// The index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MimeEntity this [int index] { - get { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - return attachments[index]; - } - set { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - attachments[index] = value; - } - } - - static void LoadContent (MimePart attachment, Stream stream) - { - var content = new MemoryBlockStream (); - var filter = new BestEncodingFilter (); - var buf = new byte[4096]; - int index, length; - int nread; - - while ((nread = stream.Read (buf, 0, buf.Length)) > 0) { - filter.Filter (buf, 0, nread, out index, out length); - content.Write (buf, 0, nread); - } - - filter.Flush (buf, 0, 0, out index, out length); - content.Position = 0; - - attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.SevenBit); - attachment.Content = new MimeContent (content); - } - - static ContentType GetMimeType (string fileName) - { - var mimeType = MimeTypes.GetMimeType (fileName); - - return ContentType.Parse (mimeType); - } - - MimeEntity CreateAttachment (ContentType contentType, string fileName, Stream stream) - { - MimeEntity attachment; - - if (contentType.IsMimeType ("message", "rfc822")) { - var message = MimeMessage.Load (stream); - var rfc822 = new MessagePart { Message = message }; - - rfc822.ContentDisposition = new ContentDisposition (linked ? ContentDisposition.Inline : ContentDisposition.Attachment); - rfc822.ContentDisposition.FileName = Path.GetFileName (fileName); - rfc822.ContentType.Name = Path.GetFileName (fileName); - - attachment = rfc822; - } else { - MimePart part; - - if (contentType.IsMimeType ("text", "*")) { - // TODO: should we try to auto-detect charsets if no charset parameter is specified? - part = new TextPart (contentType); - } else { - part = new MimePart (contentType); - } - - part.FileName = Path.GetFileName (fileName); - part.IsAttachment = !linked; - - LoadContent (part, stream); - attachment = part; - } - - if (linked) - attachment.ContentLocation = new Uri (Path.GetFileName (fileName), UriKind.Relative); - - return attachment; - } - - /// - /// Add the specified attachment. - /// - /// - /// Adds the specified data as an attachment using the supplied Content-Type. - /// The file name parameter is used to set the Content-Location. - /// For a list of known mime-types and their associated file extensions, see - /// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types - /// - /// The newly added attachment . - /// The name of the file. - /// The file data. - /// The mime-type of the file. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The specified file path is empty. - /// - public MimeEntity Add (string fileName, byte[] data, ContentType contentType) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The specified file path is empty.", nameof (fileName)); - - if (data == null) - throw new ArgumentNullException (nameof (data)); - - if (contentType == null) - throw new ArgumentNullException (nameof (contentType)); - - using (var stream = new MemoryStream (data, false)) { - var attachment = CreateAttachment (contentType, fileName, stream); - - attachments.Add (attachment); - - return attachment; - } - } - - /// - /// Add the specified attachment. - /// - /// - /// Adds the specified data as an attachment using the supplied Content-Type. - /// The file name parameter is used to set the Content-Location. - /// For a list of known mime-types and their associated file extensions, see - /// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types - /// - /// The newly added attachment . - /// The name of the file. - /// The content stream. - /// The mime-type of the file. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The specified file path is empty. - /// -or- - /// The stream cannot be read. - /// - public MimeEntity Add (string fileName, Stream stream, ContentType contentType) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The specified file path is empty.", nameof (fileName)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (!stream.CanRead) - throw new ArgumentException ("The stream cannot be read.", nameof (stream)); - - if (contentType == null) - throw new ArgumentNullException (nameof (contentType)); - - var attachment = CreateAttachment (contentType, fileName, stream); - - attachments.Add (attachment); - - return attachment; - } - - /// - /// Add the specified attachment. - /// - /// - /// Adds the data as an attachment, using the specified file name for deducing - /// the mime-type by extension and for setting the Content-Location. - /// - /// The newly added attachment . - /// The name of the file. - /// The file data to attach. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The specified file path is empty. - /// - public MimeEntity Add (string fileName, byte[] data) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The specified file path is empty.", nameof (fileName)); - - if (data == null) - throw new ArgumentNullException (nameof (data)); - - using (var stream = new MemoryStream (data, false)) { - var attachment = CreateAttachment (GetMimeType (fileName), fileName, stream); - - attachments.Add (attachment); - - return attachment; - } - } - - /// - /// Add the specified attachment. - /// - /// - /// Adds the stream as an attachment, using the specified file name for deducing - /// the mime-type by extension and for setting the Content-Location. - /// - /// The newly added attachment . - /// The name of the file. - /// The content stream. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The specified file path is empty. - /// -or- - /// The stream cannot be read - /// - public MimeEntity Add (string fileName, Stream stream) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The specified file path is empty.", nameof (fileName)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (!stream.CanRead) - throw new ArgumentException ("The stream cannot be read.", nameof (stream)); - - var attachment = CreateAttachment (GetMimeType (fileName), fileName, stream); - - attachments.Add (attachment); - - return attachment; - } - - /// - /// Add the specified attachment. - /// - /// - /// Adds the specified file as an attachment using the supplied Content-Type. - /// For a list of known mime-types and their associated file extensions, see - /// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types - /// - /// The newly added attachment . - /// The name of the file. - /// The mime-type of the file. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The specified file path is empty. - /// - /// - /// The specified file could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An I/O error occurred. - /// - public MimeEntity Add (string fileName, ContentType contentType) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The specified file path is empty.", nameof (fileName)); - - if (contentType == null) - throw new ArgumentNullException (nameof (contentType)); - - using (var stream = File.OpenRead (fileName)) { - var attachment = CreateAttachment (contentType, fileName, stream); - - attachments.Add (attachment); - - return attachment; - } - } - - /// - /// Add the specified attachment. - /// - /// - /// Adds the specified file as an attachment. - /// - /// - /// - /// - /// The newly added attachment . - /// The name of the file. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An I/O error occurred. - /// - public MimeEntity Add (string fileName) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The specified file path is empty.", nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) { - var attachment = CreateAttachment (GetMimeType (fileName), fileName, stream); - - attachments.Add (attachment); - - return attachment; - } - } - - /// - /// Add the specified attachment. - /// - /// - /// Adds the specified as an attachment. - /// - /// The attachment. - /// - /// is null. - /// - public void Add (MimeEntity attachment) - { - if (attachment == null) - throw new ArgumentNullException (nameof (attachment)); - - attachments.Add (attachment); - } - - /// - /// Clears the attachment collection. - /// - /// - /// Removes all attachments from the collection. - /// - public void Clear () - { - attachments.Clear (); - } - - /// - /// Checks if the collection contains the specified attachment. - /// - /// - /// Determines whether or not the collection contains the specified attachment. - /// - /// true if the specified attachment exists; - /// otherwise false. - /// The attachment. - /// - /// is null. - /// - public bool Contains (MimeEntity attachment) - { - if (attachment == null) - throw new ArgumentNullException (nameof (attachment)); - - return attachments.Contains (attachment); - } - - /// - /// Copies all of the attachments in the collection to the specified array. - /// - /// - /// Copies all of the attachments within the into the array, - /// starting at the specified array index. - /// - /// The array to copy the attachments to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (MimeEntity[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException (nameof (array)); - - if (arrayIndex < 0 || arrayIndex >= array.Length) - throw new ArgumentOutOfRangeException (nameof (arrayIndex)); - - attachments.CopyTo (array, arrayIndex); - } - - /// - /// Gets the index of the requested attachment, if it exists. - /// - /// - /// Finds the index of the specified attachment, if it exists. - /// - /// The index of the requested attachment; otherwise -1. - /// The attachment. - /// - /// is null. - /// - public int IndexOf (MimeEntity attachment) - { - if (attachment == null) - throw new ArgumentNullException (nameof (attachment)); - - return attachments.IndexOf (attachment); - } - - /// - /// Inserts the specified attachment at the given index. - /// - /// - /// Inserts the attachment at the specified index. - /// - /// The index to insert the attachment. - /// The attachment. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void Insert (int index, MimeEntity attachment) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (attachment == null) - throw new ArgumentNullException (nameof (attachment)); - - attachments.Insert (index, attachment); - } - - /// - /// Removes the specified attachment. - /// - /// - /// Removes the specified attachment. - /// - /// true if the attachment was removed; otherwise false. - /// The attachment. - /// - /// is null. - /// - public bool Remove (MimeEntity attachment) - { - if (attachment == null) - throw new ArgumentNullException (nameof (attachment)); - - return attachments.Remove (attachment); - } - - /// - /// Removes the attachment at the specified index. - /// - /// - /// Removes the attachment at the specified index. - /// - /// The index. - /// - /// is out of range. - /// - public void RemoveAt (int index) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - attachments.RemoveAt (index); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the list of attachments. - /// - /// - /// Gets an enumerator for the list of attachments. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return attachments.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the list of attachments. - /// - /// - /// Gets an enumerator for the list of attachments. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - } -} diff --git a/src/MimeKit/BodyBuilder.cs b/src/MimeKit/BodyBuilder.cs deleted file mode 100644 index 8aae0ef..0000000 --- a/src/MimeKit/BodyBuilder.cs +++ /dev/null @@ -1,186 +0,0 @@ -// -// BodyBuilder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A message body builder. - /// - /// - /// is a helper class for building common MIME body structures. - /// - /// - /// - /// - public class BodyBuilder - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// - /// - /// - public BodyBuilder () - { - LinkedResources = new AttachmentCollection (true); - Attachments = new AttachmentCollection (); - } - - /// - /// Get the attachments. - /// - /// - /// Represents a collection of file attachments that will be included in the message. - /// - /// - /// - /// - /// The attachments. - public AttachmentCollection Attachments { - get; private set; - } - - /// - /// Get the linked resources. - /// - /// - /// Linked resources are a special type of attachment which are linked to from the . - /// - /// - /// - /// - /// The linked resources. - public AttachmentCollection LinkedResources { - get; private set; - } - - /// - /// Get or set the text body. - /// - /// - /// Represents the plain-text formatted version of the message body. - /// - /// - /// - /// - /// The text body. - public string TextBody { - get; set; - } - - /// - /// Get or set the html body. - /// - /// - /// Represents the html formatted version of the message body and may link to any of the . - /// - /// - /// - /// - /// The html body. - public string HtmlBody { - get; set; - } - - /// - /// Construct the message body based on the text-based bodies, the linked resources, and the attachments. - /// - /// - /// Combines the , , , - /// and into the proper MIME structure suitable for display in many common - /// mail clients. - /// - /// - /// - /// - /// The message body. - public MimeEntity ToMessageBody () - { - MultipartAlternative alternative = null; - MimeEntity body = null; - - if (TextBody != null) { - var text = new TextPart ("plain"); - text.Text = TextBody; - - if (HtmlBody != null) { - alternative = new MultipartAlternative (); - alternative.Add (text); - body = alternative; - } else { - body = text; - } - } - - if (HtmlBody != null) { - var text = new TextPart ("html"); - MimeEntity html; - - text.ContentId = MimeUtils.GenerateMessageId (); - text.Text = HtmlBody; - - if (LinkedResources.Count > 0) { - var related = new MultipartRelated { - Root = text - }; - - foreach (var resource in LinkedResources) - related.Add (resource); - - html = related; - } else { - html = text; - } - - if (alternative != null) - alternative.Add (html); - else - body = html; - } - - if (Attachments.Count > 0) { - if (body == null && Attachments.Count == 1) - return Attachments[0]; - - var mixed = new Multipart ("mixed"); - - if (body != null) - mixed.Add (body); - - foreach (var attachment in Attachments) - mixed.Add (attachment); - - body = mixed; - } - - return body ?? new TextPart ("plain") { Text = string.Empty }; - } - } -} diff --git a/src/MimeKit/CancellationToken.cs b/src/MimeKit/CancellationToken.cs deleted file mode 100644 index 9b3d8d6..0000000 --- a/src/MimeKit/CancellationToken.cs +++ /dev/null @@ -1,76 +0,0 @@ -// -// CancellationToken.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2015 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace System.Threading { - public struct CancellationToken - { - public static readonly CancellationToken None = new CancellationToken (); - - public bool CanBeCancelled { - get { return false; } - } - - public bool IsCancellationRequested { - get { return false; } - } - - public void ThrowIfCancellationRequested () - { - if (IsCancellationRequested) - throw new OperationCanceledException (); - } - - public bool Equals (CancellationToken other) - { - return true; - } - - public override bool Equals (object obj) - { - if (obj is CancellationToken) - return Equals ((CancellationToken) obj); - - return false; - } - - public override int GetHashCode () - { - return base.GetHashCode (); - } - - public static bool operator == (CancellationToken left, CancellationToken right) - { - return left.Equals (right); - } - - public static bool operator != (CancellationToken left, CancellationToken right) - { - return !left.Equals (right); - } - } -} diff --git a/src/MimeKit/ContentDisposition.cs b/src/MimeKit/ContentDisposition.cs deleted file mode 100644 index 9912153..0000000 --- a/src/MimeKit/ContentDisposition.cs +++ /dev/null @@ -1,916 +0,0 @@ -// -// ContentDisposition.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A class representing a Content-Disposition header value. - /// - /// - /// The Content-Disposition header is a way for the originating client to - /// suggest to the receiving client whether to present the part to the user - /// as an attachment or as part of the content (inline). - /// - public class ContentDisposition - { - /// - /// The attachment disposition. - /// - /// - /// Indicates that the should be treated as an attachment. - /// - public const string Attachment = "attachment"; - - /// - /// The form-data disposition. - /// - /// - /// Indicates that the should be treated as form data. - /// - public const string FormData = "form-data"; - - /// - /// The inline disposition. - /// - /// - /// Indicates that the should be rendered inline. - /// - public const string Inline = "inline"; - - ParameterList parameters; - string disposition; - - /// - /// Initialize a new instance of the class. - /// - /// - /// The disposition should either be - /// or . - /// - /// The disposition. - /// - /// is null. - /// - /// - /// is not "attachment" or "inline". - /// - public ContentDisposition (string disposition) - { - Parameters = new ParameterList (); - Disposition = disposition; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// This is identical to with a disposition - /// value of . - /// - public ContentDisposition () : this (Attachment) - { - } - - static bool IsAsciiAtom (byte c) - { - return c.IsAsciiAtom (); - } - - /// - /// Get or set the disposition. - /// - /// - /// The disposition is typically either "attachment" or "inline". - /// - /// The disposition. - /// - /// is null. - /// - /// - /// is an invalid disposition value. - /// - public string Disposition { - get { return disposition; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (value.Length == 0) - throw new ArgumentException ("The disposition is not allowed to be empty.", nameof (value)); - - for (int i = 0; i < value.Length; i++) { - if (value[i] >= 127 || !IsAsciiAtom ((byte) value[i])) - throw new ArgumentException ("Illegal characters in disposition value.", nameof (value)); - } - - if (disposition == value) - return; - - disposition = value; - OnChanged (); - } - } - - /// - /// Get or set a value indicating whether the is an attachment. - /// - /// - /// A convenience property to determine if the entity should be considered an attachment or not. - /// - /// true if the is an attachment; otherwise, false. - public bool IsAttachment { - get { return disposition.Equals (Attachment, StringComparison.OrdinalIgnoreCase); } - set { disposition = value ? Attachment : Inline; } - } - - /// - /// Get the list of parameters on the . - /// - /// - /// In addition to specifying whether the entity should be treated as an - /// attachment vs displayed inline, the Content-Disposition header may also - /// contain parameters to provide further information to the receiving client - /// such as the file attributes. - /// - /// - /// - /// - /// The parameters. - public ParameterList Parameters { - get { return parameters; } - private set { - if (parameters != null) - parameters.Changed -= OnParametersChanged; - - value.Changed += OnParametersChanged; - parameters = value; - } - } - - /// - /// Get or set the name of the file. - /// - /// - /// When set, this can provide a useful hint for a default file name for the - /// content when the user decides to save it to disk. - /// - /// - /// - /// - /// The name of the file. - public string FileName { - get { return Parameters["filename"]; } - set { - if (value != null) - Parameters["filename"] = value; - else - Parameters.Remove ("filename"); - } - } - - static bool IsNullOrWhiteSpace (string value) - { - if (string.IsNullOrEmpty (value)) - return true; - - for (int i = 0; i < value.Length; i++) { - if (!char.IsWhiteSpace (value[i])) - return false; - } - - return true; - } - - /// - /// Get or set the creation-date parameter. - /// - /// - /// Refers to the date and time that the content file was created on the - /// originating system. This parameter serves little purpose and is - /// typically not used by mail clients. - /// - /// The creation date. - public DateTimeOffset? CreationDate { - get { - var value = Parameters["creation-date"]; - if (IsNullOrWhiteSpace (value)) - return null; - - var buffer = Encoding.UTF8.GetBytes (value); - DateTimeOffset ctime; - - if (!DateUtils.TryParse (buffer, 0, buffer.Length, out ctime)) - return null; - - return ctime; - } - set { - if (value.HasValue) - Parameters["creation-date"] = DateUtils.FormatDate (value.Value); - else - Parameters.Remove ("creation-date"); - } - } - - /// - /// Get or set the modification-date parameter. - /// - /// - /// Refers to the date and time that the content file was last modified on - /// the originating system. This parameter serves little purpose and is - /// typically not used by mail clients. - /// - /// The modification date. - public DateTimeOffset? ModificationDate { - get { - var value = Parameters["modification-date"]; - if (IsNullOrWhiteSpace (value)) - return null; - - var buffer = Encoding.UTF8.GetBytes (value); - DateTimeOffset mtime; - - if (!DateUtils.TryParse (buffer, 0, buffer.Length, out mtime)) - return null; - - return mtime; - } - set { - if (value.HasValue) - Parameters["modification-date"] = DateUtils.FormatDate (value.Value); - else - Parameters.Remove ("modification-date"); - } - } - - /// - /// Get or set the read-date parameter. - /// - /// - /// Refers to the date and time that the content file was last read on the - /// originating system. This parameter serves little purpose and is typically - /// not used by mail clients. - /// - /// The read date. - public DateTimeOffset? ReadDate { - get { - var value = Parameters["read-date"]; - if (IsNullOrWhiteSpace (value)) - return null; - - var buffer = Encoding.UTF8.GetBytes (value); - DateTimeOffset atime; - - if (!DateUtils.TryParse (buffer, 0, buffer.Length, out atime)) - return null; - - return atime; - } - set { - if (value.HasValue) - Parameters["read-date"] = DateUtils.FormatDate (value.Value); - else - Parameters.Remove ("read-date"); - } - } - - /// - /// Get or set the size parameter. - /// - /// - /// When set, the size parameter typically refers to the original size of the - /// content on disk. This parameter is rarely used by mail clients as it serves - /// little purpose. - /// - /// The size. - public long? Size { - get { - var value = Parameters["size"]; - if (IsNullOrWhiteSpace (value)) - return null; - - long size; - if (!long.TryParse (value, out size)) - return null; - - return size; - } - set { - if (value.HasValue) - Parameters["size"] = value.Value.ToString (); - else - Parameters.Remove ("size"); - } - } - - internal string Encode (FormatOptions options, Encoding charset) - { - int lineLength = "Content-Disposition:".Length; - var value = new StringBuilder (" "); - - value.Append (disposition); - lineLength += value.Length; - - Parameters.Encode (options, value, ref lineLength, charset); - value.Append (options.NewLine); - - return value.ToString (); - } - - /// - /// Serialize the to a string, - /// optionally encoding the parameters. - /// - /// - /// Creates a string-representation of the , - /// optionally encoding the parameters as they would be encoded for trabsport. - /// - /// The serialized string. - /// The formatting options. - /// The charset to be used when encoding the parameter values. - /// If set to true, the parameter values will be encoded. - /// - /// is null. - /// -or- - /// is null. - /// - public string ToString (FormatOptions options, Encoding charset, bool encode) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (charset == null) - throw new ArgumentNullException (nameof (charset)); - - var value = new StringBuilder ("Content-Disposition: "); - value.Append (disposition); - - if (encode) { - int lineLength = value.Length; - - Parameters.Encode (options, value, ref lineLength, charset); - } else { - value.Append (Parameters.ToString ()); - } - - return value.ToString (); - } - - /// - /// Serialize the to a string, - /// optionally encoding the parameters. - /// - /// - /// Creates a string-representation of the , - /// optionally encoding the parameters as they would be encoded for trabsport. - /// - /// The serialized string. - /// The charset to be used when encoding the parameter values. - /// If set to true, the parameter values will be encoded. - /// - /// is null. - /// - public string ToString (Encoding charset, bool encode) - { - return ToString (FormatOptions.Default, charset, encode); - } - - /// - /// Serialize the to a string. - /// - /// - /// Creates a string-representation of the . - /// - /// A that represents the current - /// . - public override string ToString () - { - return ToString (FormatOptions.Default, Encoding.UTF8, false); - } - - internal event EventHandler Changed; - - void OnParametersChanged (object sender, EventArgs e) - { - OnChanged (); - } - - void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - - internal static bool TryParse (ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out ContentDisposition disposition) - { - string type; - int atom; - - disposition = null; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Expected atom token at position {0}", index), index, index); - - return false; - } - - atom = index; - if (text[index] == '"') { - if (throwOnError) - throw new ParseException (string.Format ("Unxpected qstring token at position {0}", atom), atom, index); - - // Note: This is a work-around for broken mailers that quote the disposition value... - // - // See https://github.com/jstedfast/MailKit/issues/486 for details. - if (!ParseUtils.SkipQuoted (text, ref index, endIndex, throwOnError)) - return false; - - type = CharsetUtils.ConvertToUnicode (options, text, atom, index - atom); - type = MimeUtils.Unquote (type); - - if (string.IsNullOrEmpty (type)) - type = Attachment; - } else { - if (!ParseUtils.SkipAtom (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid atom token at position {0}", atom), atom, index); - - // Note: this is a work-around for broken mailers that do not specify a disposition value... - // - // See https://github.com/jstedfast/MailKit/issues/486 for details. - if (index > atom || text[index] != (byte) ';') - return false; - - type = Attachment; - } else { - type = Encoding.ASCII.GetString (text, atom, index - atom); - } - } - - disposition = new ContentDisposition (); - disposition.disposition = type; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) - return true; - - if (text[index] != (byte) ';') { - if (throwOnError) - throw new ParseException (string.Format ("Expected ';' at position {0}", index), index, index); - - return false; - } - - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) - return true; - - ParameterList parameters; - if (!ParameterList.TryParse (options, text, ref index, endIndex, throwOnError, out parameters)) - return false; - - disposition.Parameters = parameters; - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true if the disposition was successfully parsed; otherwise, false. - /// The parser options. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed disposition. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out ContentDisposition disposition) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int index = startIndex; - - return TryParse (options, buffer, ref index, startIndex + length, false, out disposition); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true if the disposition was successfully parsed; otherwise, false. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed disposition. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out ContentDisposition disposition) - { - return TryParse (ParserOptions.Default, buffer, startIndex, length, out disposition); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer starting at the specified index. - /// - /// true if the disposition was successfully parsed; otherwise, false. - /// The parser options. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed disposition. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, out ContentDisposition disposition) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int index = startIndex; - - return TryParse (options, buffer, ref index, buffer.Length, false, out disposition); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer starting at the specified index. - /// - /// true if the disposition was successfully parsed; otherwise, false. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed disposition. - /// - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (byte[] buffer, int startIndex, out ContentDisposition disposition) - { - return TryParse (ParserOptions.Default, buffer, startIndex, out disposition); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Disposition value from the specified buffer. - /// - /// true if the disposition was successfully parsed; otherwise, false. - /// The parser options. - /// The input buffer. - /// The parsed disposition. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, out ContentDisposition disposition) - { - ParseUtils.ValidateArguments (options, buffer); - - int index = 0; - - return TryParse (options, buffer, ref index, buffer.Length, false, out disposition); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Disposition value from the specified buffer. - /// - /// true if the disposition was successfully parsed; otherwise, false. - /// The input buffer. - /// The parsed disposition. - /// - /// is null. - /// - public static bool TryParse (byte[] buffer, out ContentDisposition disposition) - { - return TryParse (ParserOptions.Default, buffer, out disposition); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a Content-Disposition value from the supplied text. - /// - /// true if the disposition was successfully parsed; otherwise, false. - /// The parser options. - /// The text to parse. - /// The parsed disposition. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, string text, out ContentDisposition disposition) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - int index = 0; - - return TryParse (ParserOptions.Default, buffer, ref index, buffer.Length, false, out disposition); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a Content-Disposition value from the supplied text. - /// - /// true if the disposition was successfully parsed; otherwise, false. - /// The text to parse. - /// The parsed disposition. - /// - /// is null. - /// - public static bool TryParse (string text, out ContentDisposition disposition) - { - return TryParse (ParserOptions.Default, text, out disposition); - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// The parsed . - /// The parser options. - /// The input buffer. - /// The start index of the buffer. - /// The length of the buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// The could not be parsed. - /// - public static ContentDisposition Parse (ParserOptions options, byte[] buffer, int startIndex, int length) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - ContentDisposition disposition; - int index = startIndex; - - TryParse (options, buffer, ref index, startIndex + length, true, out disposition); - - return disposition; - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// The parsed . - /// The input buffer. - /// The start index of the buffer. - /// The length of the buffer. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// The could not be parsed. - /// - public static ContentDisposition Parse (byte[] buffer, int startIndex, int length) - { - return Parse (ParserOptions.Default, buffer, startIndex, length); - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer starting at the specified index. - /// - /// The parsed . - /// The parser options. - /// The input buffer. - /// The start index of the buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The could not be parsed. - /// - public static ContentDisposition Parse (ParserOptions options, byte[] buffer, int startIndex) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - ContentDisposition disposition; - int index = startIndex; - - TryParse (options, buffer, ref index, buffer.Length, true, out disposition); - - return disposition; - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer starting at the specified index. - /// - /// The parsed . - /// The input buffer. - /// The start index of the buffer. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The could not be parsed. - /// - public static ContentDisposition Parse (byte[] buffer, int startIndex) - { - return Parse (ParserOptions.Default, buffer, startIndex); - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer. - /// - /// The parsed . - /// The parser options. - /// The input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The could not be parsed. - /// - public static ContentDisposition Parse (ParserOptions options, byte[] buffer) - { - ParseUtils.ValidateArguments (options, buffer); - - ContentDisposition disposition; - int index = 0; - - TryParse (options, buffer, ref index, buffer.Length, true, out disposition); - - return disposition; - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Disposition value from the supplied buffer. - /// - /// The parsed . - /// The input buffer. - /// - /// is null. - /// - /// - /// The could not be parsed. - /// - public static ContentDisposition Parse (byte[] buffer) - { - return Parse (ParserOptions.Default, buffer); - } - - /// - /// Parse the specified text into a new instance of the class. - /// - /// - /// Parses a Content-Disposition value from the specified text. - /// - /// The parsed . - /// The parser options. - /// The input text. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The could not be parsed. - /// - public static ContentDisposition Parse (ParserOptions options, string text) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - ContentDisposition disposition; - int index = 0; - - TryParse (options, buffer, ref index, buffer.Length, true, out disposition); - - return disposition; - } - - /// - /// Parse the specified text into a new instance of the class. - /// - /// - /// Parses a Content-Disposition value from the specified text. - /// - /// The parsed . - /// The input text. - /// - /// is null. - /// - /// - /// The could not be parsed. - /// - public static ContentDisposition Parse (string text) - { - return Parse (ParserOptions.Default, text); - } - } -} diff --git a/src/MimeKit/ContentEncoding.cs b/src/MimeKit/ContentEncoding.cs deleted file mode 100644 index 53c6cbd..0000000 --- a/src/MimeKit/ContentEncoding.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// ContentEncoding.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit { - /// - /// An enumeration of all supported content transfer encodings. - /// - /// - /// Some older mail software is unable to properly deal with - /// data outside of the ASCII range, so it is sometimes - /// necessary to encode the content of MIME entities. - /// - /// - public enum ContentEncoding { - /// - /// The default encoding (aka no encoding at all). - /// - Default, - - /// - /// The 7bit content transfer encoding. This encoding should be restricted to textual content - /// in the US-ASCII range. - /// - SevenBit, - - /// - /// The 8bit content transfer encoding. This encoding should be restricted to textual content - /// outside of the US-ASCII range but may not be supported by all transport services such as - /// older SMTP servers that do not support the 8BITMIME extension. - /// - EightBit, - - /// - /// The binary content transfer encoding. This encoding is simply unencoded binary data. Typically - /// not supported by standard message transport services such as SMTP. - /// - Binary, - - /// - /// The base64 content transfer encoding. This encoding is typically used for encoding binary data - /// or textual content in a largely 8bit charset encoding and is supported by all message transport - /// services. - /// - Base64, - - /// - /// The quoted-printable content transfer encoding. This encoding is used for textual content that - /// is in a charset that has a minority of characters outside of the US-ASCII range (such as - /// ISO-8859-1 and other single-byte charset encodings) and is supported by all message transport - /// services. - /// - QuotedPrintable, - - /// - /// The uuencode content transfer encoding. This is an obsolete encoding meant for encoding binary - /// data and has largely been superceeded by . - /// - UUEncode, - } -} diff --git a/src/MimeKit/ContentType.cs b/src/MimeKit/ContentType.cs deleted file mode 100644 index 02244da..0000000 --- a/src/MimeKit/ContentType.cs +++ /dev/null @@ -1,881 +0,0 @@ -// -// ContentType.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A class representing a Content-Type header value. - /// - /// - /// The Content-Type header is a way for the originating client to - /// suggest to the receiving client the mime-type of the content and, - /// depending on that mime-type, presentation options such as charset. - /// - public class ContentType - { - ParameterList parameters; - string type, subtype; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new based on the media type and subtype provided. - /// - /// Media type. - /// Media subtype. - /// - /// is null. - /// -or- - /// is null. - /// - public ContentType (string mediaType, string mediaSubtype) - { - if (mediaType == null) - throw new ArgumentNullException (nameof (mediaType)); - - if (mediaSubtype == null) - throw new ArgumentNullException (nameof (mediaSubtype)); - - Parameters = new ParameterList (); - subtype = mediaSubtype; - type = mediaType; - } - - /// - /// Get or set the type of the media. - /// - /// - /// Represents the media type of the . Examples include - /// "text", "image", and "application". This string should - /// always be treated as case-insensitive. - /// - /// The type of the media. - /// - /// is null. - /// - public string MediaType { - get { return type; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (type == value) - return; - - type = value; - - OnChanged (); - } - } - - /// - /// Get or set the media subtype. - /// - /// - /// Represents the media subtype of the . Examples include - /// "html", "jpeg", and "octet-stream". This string should - /// always be treated as case-insensitive. - /// - /// The media subtype. - /// - /// is null. - /// - public string MediaSubtype { - get { return subtype; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (subtype == value) - return; - - subtype = value; - - OnChanged (); - } - } - - /// - /// Get the list of parameters on the . - /// - /// - /// In addition to the media type and subtype, the Content-Type header may also - /// contain parameters to provide further hints to the receiving client as to - /// how to process or display the content. - /// - /// The parameters. - public ParameterList Parameters { - get { return parameters; } - internal set { - if (parameters != null) - parameters.Changed -= OnParametersChanged; - - value.Changed += OnParametersChanged; - parameters = value; - } - } - - /// - /// Get or set the boundary parameter. - /// - /// - /// This is a special parameter on entities, designating to the - /// parser a unique string that should be considered the boundary marker for each sub-part. - /// - /// The boundary. - public string Boundary { - get { return Parameters["boundary"]; } - set { - if (value != null) - Parameters["boundary"] = value; - else - Parameters.Remove ("boundary"); - } - } - - /// - /// Get or set the charset parameter. - /// - /// - /// Text-based entities will often include a charset parameter - /// so that the receiving client can properly render the text. - /// - /// The charset. - public string Charset { - get { return Parameters["charset"]; } - set { - if (value != null) - Parameters["charset"] = value; - else - Parameters.Remove ("charset"); - } - } - - /// - /// Get or set the charset parameter as an . - /// - /// - /// Text-based entities will often include a charset parameter - /// so that the receiving client can properly render the text. - /// - /// The charset encoding. - public Encoding CharsetEncoding { - get { - var charset = Charset; - - if (charset == null) - return null; - - try { - return CharsetUtils.GetEncoding (charset); - } catch { - return null; - } - } - set { - Charset = value != null ? CharsetUtils.GetMimeCharset (value) : null; - } - } - - /// - /// Get or set the format parameter. - /// - /// - /// The format parameter is typically use with text/plain - /// entities and will either have a value of "fixed" or "flowed". - /// - /// The charset. - public string Format { - get { return Parameters["format"]; } - set { - if (value != null) - Parameters["format"] = value; - else - Parameters.Remove ("format"); - } - } - - /// - /// Gets the simple mime-type. - /// - /// - /// Gets the simple mime-type. - /// - /// The mime-type. - public string MimeType { - get { return string.Format ("{0}/{1}", MediaType, MediaSubtype); } - } - - /// - /// Get or set the name parameter. - /// - /// - /// The name parameter is a way for the originiating client to suggest - /// to the receiving client a display-name for the content, which may - /// be used by the receiving client if it cannot display the actual - /// content to the user. - /// - /// The name. - public string Name { - get { return Parameters["name"]; } - set { - if (value != null) - Parameters["name"] = value; - else - Parameters.Remove ("name"); - } - } - - /// - /// Check if the this instance of matches - /// the specified MIME media type and subtype. - /// - /// - /// If the specified or - /// are "*", they match anything. - /// - /// true if the matches the - /// provided media type and subtype. - /// The media type. - /// The media subtype. - /// - /// is null. - /// -or- - /// is null. - /// - public bool IsMimeType (string mediaType, string mediaSubtype) - { - if (mediaType == null) - throw new ArgumentNullException (nameof (mediaType)); - - if (mediaSubtype == null) - throw new ArgumentNullException (nameof (mediaSubtype)); - - if (mediaType == "*" || mediaType.Equals (type, StringComparison.OrdinalIgnoreCase)) - return mediaSubtype == "*" || mediaSubtype.Equals (subtype, StringComparison.OrdinalIgnoreCase); - - return false; - } - - internal string Encode (FormatOptions options, Encoding charset) - { - int lineLength = "Content-Type:".Length; - var value = new StringBuilder (" "); - - value.Append (MediaType); - value.Append ('/'); - value.Append (MediaSubtype); - - lineLength += value.Length; - - Parameters.Encode (options, value, ref lineLength, charset); - value.Append (options.NewLine); - - return value.ToString (); - } - - /// - /// Serialize the to a string, - /// optionally encoding the parameters. - /// - /// - /// Creates a string-representation of the , optionally encoding - /// the parameters as they would be encoded for transport. - /// - /// The serialized string. - /// The formatting options. - /// The charset to be used when encoding the parameter values. - /// If set to true, the parameter values will be encoded. - /// - /// is null. - /// -or- - /// is null. - /// - public string ToString (FormatOptions options, Encoding charset, bool encode) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (charset == null) - throw new ArgumentNullException (nameof (charset)); - - var value = new StringBuilder ("Content-Type: "); - value.Append (MediaType); - value.Append ('/'); - value.Append (MediaSubtype); - - if (encode) { - int lineLength = value.Length; - - Parameters.Encode (options, value, ref lineLength, charset); - } else { - value.Append (Parameters.ToString ()); - } - - return value.ToString (); - } - - /// - /// Serialize the to a string, - /// optionally encoding the parameters. - /// - /// - /// Creates a string-representation of the , optionally encoding - /// the parameters as they would be encoded for transport. - /// - /// The serialized string. - /// The charset to be used when encoding the parameter values. - /// If set to true, the parameter values will be encoded. - /// - /// is null. - /// - public string ToString (Encoding charset, bool encode) - { - return ToString (FormatOptions.Default, charset, encode); - } - - /// - /// Serialize the to a string. - /// - /// - /// Creates a string-representation of the . - /// - /// A that represents the current - /// . - public override string ToString () - { - return ToString (FormatOptions.Default, Encoding.UTF8, false); - } - - internal event EventHandler Changed; - - void OnParametersChanged (object sender, EventArgs e) - { - OnChanged (); - } - - void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - - static bool SkipType (byte[] text, ref int index, int endIndex) - { - int startIndex = index; - - while (index < endIndex && text[index].IsAsciiAtom () && text[index] != (byte) '/') - index++; - - return index > startIndex; - } - - internal static bool TryParse (ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out ContentType contentType) - { - string type, subtype; - int start; - - contentType = null; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - start = index; - if (!SkipType (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid type token at position {0}", start), start, index); - - return false; - } - - type = Encoding.ASCII.GetString (text, start, index - start); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex || text[index] != (byte) '/') { - if (throwOnError) - throw new ParseException (string.Format ("Expected '/' at position {0}", index), index, index); - - return false; - } - - // skip over the '/' - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - start = index; - if (!ParseUtils.SkipToken (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid atom token at position {0}", start), start, index); - - return false; - } - - subtype = Encoding.ASCII.GetString (text, start, index - start); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - contentType = new ContentType (type, subtype); - - if (index >= endIndex) - return true; - - if (text[index] != (byte) ';') { - if (throwOnError) - throw new ParseException (string.Format ("Expected ';' at position {0}", index), index, index); - - return false; - } - - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) - return true; - - ParameterList parameters; - if (!ParameterList.TryParse (options, text, ref index, endIndex, throwOnError, out parameters)) - return false; - - contentType.Parameters = parameters; - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Type value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true if the content type was successfully parsed; otherwise, false. - /// The parser options. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed content type. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out ContentType type) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int index = startIndex; - - return TryParse (options, buffer, ref index, startIndex + length, false, out type); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Type value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true if the content type was successfully parsed; otherwise, false. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed content type. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out ContentType type) - { - return TryParse (ParserOptions.Default, buffer, startIndex, length, out type); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Type value from the supplied buffer starting at the specified index. - /// - /// true if the content type was successfully parsed; otherwise, false. - /// The parser options. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed content type. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, out ContentType type) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int index = startIndex; - - return TryParse (options, buffer, ref index, buffer.Length, false, out type); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Type value from the supplied buffer starting at the specified index. - /// - /// true if the content type was successfully parsed; otherwise, false. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed content type. - /// - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (byte[] buffer, int startIndex, out ContentType type) - { - return TryParse (ParserOptions.Default, buffer, startIndex, out type); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Type value from the specified buffer. - /// - /// true if the content type was successfully parsed; otherwise, false. - /// The parser options. - /// The input buffer. - /// The parsed content type. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, out ContentType type) - { - ParseUtils.ValidateArguments (options, buffer); - - int index = 0; - - return TryParse (options, buffer, ref index, buffer.Length, false, out type); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a Content-Type value from the specified buffer. - /// - /// true if the content type was successfully parsed; otherwise, false. - /// The input buffer. - /// The parsed content type. - /// - /// is null. - /// - public static bool TryParse (byte[] buffer, out ContentType type) - { - return TryParse (ParserOptions.Default, buffer, out type); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a Content-Type value from the specified text. - /// - /// true if the content type was successfully parsed; otherwise, false. - /// THe parser options. - /// The text to parse. - /// The parsed content type. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, string text, out ContentType type) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - int index = 0; - - return TryParse (options, buffer, ref index, buffer.Length, false, out type); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a Content-Type value from the specified text. - /// - /// true if the content type was successfully parsed; otherwise, false. - /// The text to parse. - /// The parsed content type. - /// - /// is null. - /// - public static bool TryParse (string text, out ContentType type) - { - return TryParse (ParserOptions.Default, text, out type); - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Type value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// The parsed . - /// The parser options. - /// The input buffer. - /// The start index of the buffer. - /// The length of the buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// The could not be parsed. - /// - public static ContentType Parse (ParserOptions options, byte[] buffer, int startIndex, int length) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int index = startIndex; - ContentType type; - - TryParse (options, buffer, ref index, startIndex + length, true, out type); - - return type; - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Type value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// The parsed . - /// The input buffer. - /// The start index of the buffer. - /// The length of the buffer. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// The could not be parsed. - /// - public static ContentType Parse (byte[] buffer, int startIndex, int length) - { - return Parse (ParserOptions.Default, buffer, startIndex, length); - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Type value from the supplied buffer starting at the specified index. - /// - /// The parsed . - /// The parser options. - /// The input buffer. - /// The start index of the buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The could not be parsed. - /// - public static ContentType Parse (ParserOptions options, byte[] buffer, int startIndex) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int index = startIndex; - ContentType type; - - TryParse (options, buffer, ref index, buffer.Length, true, out type); - - return type; - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Type value from the supplied buffer starting at the specified index. - /// - /// The parsed . - /// The input buffer. - /// The start index of the buffer. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The could not be parsed. - /// - public static ContentType Parse (byte[] buffer, int startIndex) - { - return Parse (ParserOptions.Default, buffer, startIndex); - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Type value from the specified buffer. - /// - /// The parsed . - /// The parser options. - /// The input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The could not be parsed. - /// - public static ContentType Parse (ParserOptions options, byte[] buffer) - { - ParseUtils.ValidateArguments (options, buffer); - - ContentType type; - int index = 0; - - TryParse (options, buffer, ref index, buffer.Length, true, out type); - - return type; - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses a Content-Type value from the specified buffer. - /// - /// The parsed . - /// The input buffer. - /// - /// is null. - /// - /// - /// The could not be parsed. - /// - public static ContentType Parse (byte[] buffer) - { - return Parse (ParserOptions.Default, buffer); - } - - /// - /// Parse the specified text into a new instance of the class. - /// - /// - /// Parses a Content-Type value from the specified text. - /// - /// The parsed . - /// The parser options. - /// The text. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The could not be parsed. - /// - public static ContentType Parse (ParserOptions options, string text) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - ContentType type; - int index = 0; - - TryParse (options, buffer, ref index, buffer.Length, true, out type); - - return type; - } - - /// - /// Parse the specified text into a new instance of the class. - /// - /// - /// Parses a Content-Type value from the specified text. - /// - /// The parsed . - /// The text. - /// - /// is null. - /// - /// - /// The could not be parsed. - /// - public static ContentType Parse (string text) - { - return Parse (ParserOptions.Default, text); - } - } -} diff --git a/src/MimeKit/Cryptography/ApplicationPgpEncrypted.cs b/src/MimeKit/Cryptography/ApplicationPgpEncrypted.cs deleted file mode 100644 index 05859d6..0000000 --- a/src/MimeKit/Cryptography/ApplicationPgpEncrypted.cs +++ /dev/null @@ -1,97 +0,0 @@ -// -// ApplicationPgpEncrypted.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; - -namespace MimeKit.Cryptography { - /// - /// A MIME part with a Content-Type of application/pgp-encrypted. - /// - /// - /// An application/pgp-encrypted part will typically be the first child of - /// a part and contains only a Version - /// header. - /// - public class ApplicationPgpEncrypted : MimePart - { - /// - /// Initialize a new instance of the - /// class based on the . - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public ApplicationPgpEncrypted (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new MIME part with a Content-Type of application/pgp-encrypted - /// and content matching "Version: 1\n". - /// - public ApplicationPgpEncrypted () : base ("application", "pgp-encrypted") - { - ContentDisposition = new ContentDisposition ("attachment"); - ContentTransferEncoding = ContentEncoding.SevenBit; - - var content = new MemoryStream (Encoding.UTF8.GetBytes ("Version: 1\n"), false); - - Content = new MimeContent (content); - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitApplicationPgpEncrypted (this); - } - } -} diff --git a/src/MimeKit/Cryptography/ApplicationPgpSignature.cs b/src/MimeKit/Cryptography/ApplicationPgpSignature.cs deleted file mode 100644 index d7590e3..0000000 --- a/src/MimeKit/Cryptography/ApplicationPgpSignature.cs +++ /dev/null @@ -1,106 +0,0 @@ -// -// ApplicationPgpSignature.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; - -namespace MimeKit.Cryptography { - /// - /// A MIME part with a Content-Type of application/pgp-signature. - /// - /// - /// An application/pgp-signature part contains detatched pgp signature data - /// and is typically contained within a part. - /// To verify the signature, use the one of the - /// Verify - /// methods on the parent multipart/signed part. - /// - public class ApplicationPgpSignature : MimePart - { - /// - /// Initialize a new instance of the - /// class based on the . - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public ApplicationPgpSignature (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the - /// class with a Content-Type of application/pgp-signature. - /// - /// - /// Creates a new MIME part with a Content-Type of application/pgp-signature - /// and the as its content. - /// - /// The content stream. - /// - /// is null. - /// - /// - /// does not support reading. - /// -or- - /// does not support seeking. - /// - public ApplicationPgpSignature (Stream stream) : base ("application", "pgp-signature") - { - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment); - ContentTransferEncoding = ContentEncoding.SevenBit; - Content = new MimeContent (stream); - FileName = "signature.pgp"; - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitApplicationPgpSignature (this); - } - } -} diff --git a/src/MimeKit/Cryptography/ApplicationPkcs7Mime.cs b/src/MimeKit/Cryptography/ApplicationPkcs7Mime.cs deleted file mode 100644 index 12ebd80..0000000 --- a/src/MimeKit/Cryptography/ApplicationPkcs7Mime.cs +++ /dev/null @@ -1,924 +0,0 @@ -// -// ApplicationPkcs7Mime.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Collections.Generic; - -using MimeKit.IO; - -namespace MimeKit.Cryptography { - /// - /// An S/MIME part with a Content-Type of application/pkcs7-mime. - /// - /// - /// An application/pkcs7-mime is an S/MIME part and may contain encrypted, - /// signed or compressed data (or any combination of the above). - /// - public class ApplicationPkcs7Mime : MimePart - { - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public ApplicationPkcs7Mime (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new MIME part with a Content-Type of application/pkcs7-mime - /// and the as its content. - /// Unless you are writing your own pkcs7 implementation, you'll probably - /// want to use the , - /// , and/or - /// method to create new instances - /// of this class. - /// - /// The S/MIME type. - /// The content stream. - /// - /// is null. - /// - /// - /// is not a valid value. - /// - /// - /// does not support reading. - /// -or- - /// does not support seeking. - /// - public ApplicationPkcs7Mime (SecureMimeType type, Stream stream) : base ("application", "pkcs7-mime") - { - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment); - ContentTransferEncoding = ContentEncoding.Base64; - Content = new MimeContent (stream); - - switch (type) { - case SecureMimeType.CompressedData: - ContentType.Parameters["smime-type"] = "compressed-data"; - ContentDisposition.FileName = "smime.p7z"; - ContentType.Name = "smime.p7z"; - break; - case SecureMimeType.EnvelopedData: - ContentType.Parameters["smime-type"] = "enveloped-data"; - ContentDisposition.FileName = "smime.p7m"; - ContentType.Name = "smime.p7m"; - break; - case SecureMimeType.SignedData: - ContentType.Parameters["smime-type"] = "signed-data"; - ContentDisposition.FileName = "smime.p7m"; - ContentType.Name = "smime.p7m"; - break; - case SecureMimeType.CertsOnly: - ContentType.Parameters["smime-type"] = "certs-only"; - ContentDisposition.FileName = "smime.p7c"; - ContentType.Name = "smime.p7c"; - break; - default: - throw new ArgumentOutOfRangeException (nameof (type)); - } - } - - /// - /// Gets the value of the "smime-type" parameter. - /// - /// - /// Gets the value of the "smime-type" parameter. - /// - /// The value of the "smime-type" parameter. - public SecureMimeType SecureMimeType { - get { - var type = ContentType.Parameters["smime-type"]; - - if (type == null) - return SecureMimeType.Unknown; - - switch (type.ToLowerInvariant ()) { - case "authenveloped-data": return SecureMimeType.AuthEnvelopedData; - case "compressed-data": return SecureMimeType.CompressedData; - case "enveloped-data": return SecureMimeType.EnvelopedData; - case "signed-data": return SecureMimeType.SignedData; - case "certs-only": return SecureMimeType.CertsOnly; - default: return SecureMimeType.Unknown; - } - } - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitApplicationPkcs7Mime (this); - } - - /// - /// Decompress the compressed-data. - /// - /// - /// Decompresses the compressed-data using the specified . - /// - /// The decompressed . - /// The S/MIME context to use for decompressing. - /// - /// is null. - /// - /// - /// The "smime-type" parameter on the Content-Type header is not "compressed-data". - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public MimeEntity Decompress (SecureMimeContext ctx) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (SecureMimeType != SecureMimeType.CompressedData && SecureMimeType != SecureMimeType.Unknown) - throw new InvalidOperationException (); - - using (var memory = new MemoryBlockStream ()) { - Content.DecodeTo (memory); - memory.Position = 0; - - return ctx.Decompress (memory); - } - } - - /// - /// Decompress the compressed-data. - /// - /// - /// Decompresses the compressed-data using the default . - /// - /// The decompressed . - /// - /// The "smime-type" parameter on the Content-Type header is not "compressed-data". - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public MimeEntity Decompress () - { - if (SecureMimeType != SecureMimeType.CompressedData && SecureMimeType != SecureMimeType.Unknown) - throw new InvalidOperationException (); - - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return Decompress (ctx); - } - - /// - /// Decrypt the enveloped-data. - /// - /// - /// Decrypts the enveloped-data using the specified . - /// - /// The decrypted . - /// The S/MIME context to use for decrypting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The "smime-type" parameter on the Content-Type header is not "enveloped-data". - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public MimeEntity Decrypt (SecureMimeContext ctx, CancellationToken cancellationToken = default (CancellationToken)) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (SecureMimeType != SecureMimeType.EnvelopedData && SecureMimeType != SecureMimeType.Unknown) - throw new InvalidOperationException (); - - using (var memory = new MemoryBlockStream ()) { - Content.DecodeTo (memory); - memory.Position = 0; - - return ctx.Decrypt (memory, cancellationToken); - } - } - - /// - /// Decrypt the enveloped-data. - /// - /// - /// Decrypts the enveloped-data using the default . - /// - /// The decrypted . - /// The cancellation token. - /// - /// The "smime-type" parameter on the Content-Type header is not "certs-only". - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public MimeEntity Decrypt (CancellationToken cancellationToken = default (CancellationToken)) - { - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return Decrypt (ctx, cancellationToken); - } - - /// - /// Import the certificates contained in the application/pkcs7-mime content. - /// - /// - /// Imports the certificates contained in the application/pkcs7-mime content. - /// - /// The S/MIME context to import certificates into. - /// - /// is null. - /// - /// - /// The "smime-type" parameter on the Content-Type header is not "certs-only". - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public void Import (SecureMimeContext ctx) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (SecureMimeType != SecureMimeType.CertsOnly && SecureMimeType != SecureMimeType.Unknown) - throw new InvalidOperationException (); - - using (var memory = new MemoryBlockStream ()) { - Content.DecodeTo (memory); - memory.Position = 0; - - ctx.Import (memory); - } - } - - /// - /// Verify the signed-data and return the unencapsulated . - /// - /// - /// Verifies the signed-data and returns the unencapsulated . - /// - /// The list of digital signatures. - /// The S/MIME context to use for verifying the signature. - /// The unencapsulated entity. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The "smime-type" parameter on the Content-Type header is not "signed-data". - /// - /// - /// The extracted content could not be parsed as a MIME entity. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public DigitalSignatureCollection Verify (SecureMimeContext ctx, out MimeEntity entity, CancellationToken cancellationToken = default (CancellationToken)) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (SecureMimeType != SecureMimeType.SignedData && SecureMimeType != SecureMimeType.Unknown) - throw new InvalidOperationException (); - - using (var memory = new MemoryBlockStream ()) { - Content.DecodeTo (memory); - memory.Position = 0; - - return ctx.Verify (memory, out entity, cancellationToken); - } - } - - /// - /// Verifies the signed-data and returns the unencapsulated . - /// - /// - /// Verifies the signed-data using the default and returns the - /// unencapsulated . - /// - /// The list of digital signatures. - /// The unencapsulated entity. - /// The cancellation token. - /// - /// The "smime-type" parameter on the Content-Type header is not "signed-data". - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public DigitalSignatureCollection Verify (out MimeEntity entity, CancellationToken cancellationToken = default (CancellationToken)) - { - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return Verify (ctx, out entity, cancellationToken); - } - - /// - /// Compresses the specified entity. - /// - /// - /// Compresses the specified entity using the specified . - /// Most mail clients, even among those that support S/MIME, do not support compression. - /// - /// The compressed entity. - /// The S/MIME context to use for compressing. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Compress (SecureMimeContext ctx, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - return ctx.Compress (memory); - } - } - - /// - /// Compresses the specified entity. - /// - /// - /// Compresses the specified entity using the default . - /// Most mail clients, even among those that support S/MIME, do not support compression. - /// - /// The compressed entity. - /// The entity. - /// - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Compress (MimeEntity entity) - { - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return Compress (ctx, entity); - } - - /// - /// Encrypts the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients using the supplied . - /// - /// The encrypted entity. - /// The S/MIME context to use for encrypting. - /// The recipients. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Encrypt (SecureMimeContext ctx, CmsRecipientCollection recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - return ctx.Encrypt (recipients, memory); - } - } - - /// - /// Encrypts the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients using the default . - /// - /// The encrypted entity. - /// The recipients. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, MimeEntity entity) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return Encrypt (ctx, recipients, entity); - } - - /// - /// Encrypts the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients using the supplied . - /// - /// The encrypted entity. - /// The S/MIME context to use for encrypting. - /// The recipients. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// Valid certificates could not be found for one or more of the . - /// - /// - /// A certificate could not be found for one or more of the . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Encrypt (SecureMimeContext ctx, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - return (ApplicationPkcs7Mime) ctx.Encrypt (recipients, memory); - } - } - - /// - /// Encrypts the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients using the default . - /// - /// The encrypted entity. - /// The recipients. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// Valid certificates could not be found for one or more of the . - /// - /// - /// A certificate could not be found for one or more of the . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Encrypt (IEnumerable recipients, MimeEntity entity) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return Encrypt (ctx, recipients, entity); - } - - /// - /// Cryptographically signs the specified entity. - /// - /// - /// Signs the entity using the supplied signer and . - /// For better interoperability with other mail clients, you should use - /// - /// instead as the multipart/signed format is supported among a much larger - /// subset of mail client software. - /// - /// The signed entity. - /// The S/MIME context to use for signing. - /// The signer. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Sign (SecureMimeContext ctx, CmsSigner signer, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - return ctx.EncapsulatedSign (signer, memory); - } - } - - /// - /// Cryptographically signs the specified entity. - /// - /// - /// Signs the entity using the supplied signer and the default . - /// For better interoperability with other mail clients, you should use - /// - /// instead as the multipart/signed format is supported among a much larger - /// subset of mail client software. - /// - /// The signed entity. - /// The signer. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Sign (CmsSigner signer, MimeEntity entity) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return Sign (ctx, signer, entity); - } - - /// - /// Cryptographically signs the specified entity. - /// - /// - /// Signs the entity using the supplied signer, digest algorithm and . - /// For better interoperability with other mail clients, you should use - /// - /// instead as the multipart/signed format is supported among a much larger - /// subset of mail client software. - /// - /// The signed entity. - /// The S/MIME context to use for signing. - /// The signer. - /// The digest algorithm to use for signing. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// A signing certificate could not be found for . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Sign (SecureMimeContext ctx, MailboxAddress signer, DigestAlgorithm digestAlgo, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - return ctx.EncapsulatedSign (signer, digestAlgo, memory); - } - } - - /// - /// Cryptographically signs the specified entity. - /// - /// - /// Signs the entity using the supplied signer, digest algorithm and the default - /// . - /// For better interoperability with other mail clients, you should use - /// - /// instead as the multipart/signed format is supported among a much larger - /// subset of mail client software. - /// - /// The signed entity. - /// The signer. - /// The digest algorithm to use for signing. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// A signing certificate could not be found for . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime Sign (MailboxAddress signer, DigestAlgorithm digestAlgo, MimeEntity entity) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return Sign (ctx, signer, digestAlgo, entity); - } - - /// - /// Cryptographically signs and encrypts the specified entity. - /// - /// - /// Cryptographically signs entity using the supplied signer and then - /// encrypts the result to the specified recipients. - /// - /// The signed and encrypted entity. - /// The S/MIME context to use for signing and encrypting. - /// The signer. - /// The recipients. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime SignAndEncrypt (SecureMimeContext ctx, CmsSigner signer, CmsRecipientCollection recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - return Encrypt (ctx, recipients, MultipartSigned.Create (ctx, signer, entity)); - } - - /// - /// Cryptographically signs and encrypts the specified entity. - /// - /// - /// Cryptographically signs entity using the supplied signer and the default - /// and then encrypts the result to the specified recipients. - /// - /// The signed and encrypted entity. - /// The signer. - /// The recipients. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime SignAndEncrypt (CmsSigner signer, CmsRecipientCollection recipients, MimeEntity entity) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return SignAndEncrypt (ctx, signer, recipients, entity); - } - - /// - /// Cryptographically signs and encrypts the specified entity. - /// - /// - /// Cryptographically signs entity using the supplied signer and then - /// encrypts the result to the specified recipients. - /// - /// The signed and encrypted entity. - /// The S/MIME context to use for signing and encrypting. - /// The signer. - /// The digest algorithm to use for signing. - /// The recipients. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// A signing certificate could not be found for . - /// -or- - /// A certificate could not be found for one or more of the . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime SignAndEncrypt (SecureMimeContext ctx, MailboxAddress signer, DigestAlgorithm digestAlgo, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - return Encrypt (ctx, recipients, MultipartSigned.Create (ctx, signer, digestAlgo, entity)); - } - - /// - /// Cryptographically signs and encrypts the specified entity. - /// - /// - /// Cryptographically signs entity using the supplied signer and the default - /// and then encrypts the result to the specified recipients. - /// - /// The signed and encrypted entity. - /// The signer. - /// The digest algorithm to use for signing. - /// The recipients. - /// The entity. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// A signing certificate could not be found for . - /// -or- - /// A certificate could not be found for one or more of the . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static ApplicationPkcs7Mime SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, IEnumerable recipients, MimeEntity entity) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-mime")) - return SignAndEncrypt (ctx, signer, digestAlgo, recipients, entity); - } - } -} diff --git a/src/MimeKit/Cryptography/ApplicationPkcs7Signature.cs b/src/MimeKit/Cryptography/ApplicationPkcs7Signature.cs deleted file mode 100644 index 200d503..0000000 --- a/src/MimeKit/Cryptography/ApplicationPkcs7Signature.cs +++ /dev/null @@ -1,105 +0,0 @@ -// -// ApplicationPkcs7Signature.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; - -namespace MimeKit.Cryptography { - /// - /// An S/MIME part with a Content-Type of application/pkcs7-signature. - /// - /// - /// An application/pkcs7-signature part contains detatched pkcs7 signature data - /// and is typically contained within a part. - /// To verify the signature, use one of the - /// Verify - /// methods on the parent multipart/signed part. - /// - public class ApplicationPkcs7Signature : MimePart - { - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public ApplicationPkcs7Signature (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the - /// class with a Content-Type of application/pkcs7-signature. - /// - /// - /// Creates a new MIME part with a Content-Type of application/pkcs7-signature - /// and the as its content. - /// - /// The content stream. - /// - /// is null. - /// - /// - /// does not support reading. - /// -or- - /// does not support seeking. - /// - public ApplicationPkcs7Signature (Stream stream) : base ("application", "pkcs7-signature") - { - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment); - ContentTransferEncoding = ContentEncoding.Base64; - Content = new MimeContent (stream); - FileName = "smime.p7s"; - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitApplicationPkcs7Signature (this); - } - } -} diff --git a/src/MimeKit/Cryptography/ArcSigner.cs b/src/MimeKit/Cryptography/ArcSigner.cs deleted file mode 100644 index 4f6fbb3..0000000 --- a/src/MimeKit/Cryptography/ArcSigner.cs +++ /dev/null @@ -1,729 +0,0 @@ -// -// ArcSigner.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -using Org.BouncyCastle.Crypto; - -using MimeKit.IO; -using MimeKit.Utils; - -namespace MimeKit.Cryptography { - /// - /// An ARC signer. - /// - /// - /// An ARC signer. - /// - /// - /// - /// - public abstract class ArcSigner : DkimSignerBase - { - static readonly string[] ArcShouldNotInclude = { "return-path", "received", "comments", "keywords", "bcc", "resent-bcc", "arc-seal" }; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// - protected ArcSigner (string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : base (domain, selector, algorithm) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The signer's private key. - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a private key. - /// - protected ArcSigner (AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (!key.IsPrivate) - throw new ArgumentException ("The key must be a private key.", nameof (key)); - - PrivateKey = key; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// - /// - /// - /// The file containing the private key. - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The file did not contain a private key. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An I/O error occurred. - /// - protected ArcSigner (string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The file name cannot be empty.", nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - PrivateKey = LoadPrivateKey (stream); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The stream containing the private key. - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The file did not contain a private key. - /// - /// - /// An I/O error occurred. - /// - protected ArcSigner (Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - PrivateKey = LoadPrivateKey (stream); - } - - /// - /// Generate an ARC-Authentication-Results header. - /// - /// - /// Generates an ARC-Authentication-Results header. - /// If the returned contains a - /// with a equal to "arc", then the - /// will be used as the cv= tag value - /// in the ARC-Seal header generated by the . - /// - /// - /// - /// - /// The format options. - /// The message to create the ARC-Authentication-Results header for. - /// The cancellation token. - /// The ARC-Authentication-Results header or null if the should not sign the message. - protected abstract AuthenticationResults GenerateArcAuthenticationResults (FormatOptions options, MimeMessage message, CancellationToken cancellationToken); - - /// - /// Asynchronously generate an ARC-Authentication-Results header. - /// - /// - /// Asynchronously generates an ARC-Authentication-Results header. - /// If the returned contains a - /// with a equal to "arc", then the - /// will be used as the cv= tag value - /// in the ARC-Seal header generated by the . - /// - /// - /// - /// - /// The format options. - /// The message to create the ARC-Authentication-Results header for. - /// The cancellation token. - /// The ARC-Authentication-Results header or null if the should not sign the message. - protected abstract Task GenerateArcAuthenticationResultsAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken); - - /// - /// Get the timestamp value. - /// - /// - /// Gets the timestamp to use as the t= value in the ARC-Message-Signature and ARC-Seal headers. - /// - /// A value representing the timestamp value. - protected virtual long GetTimestamp () - { - return (long) (DateTime.UtcNow - DateUtils.UnixEpoch).TotalSeconds; - } - - StringBuilder CreateArcHeaderBuilder (int instance) - { - var value = new StringBuilder (); - - value.AppendFormat ("i={0}", instance.ToString (CultureInfo.InvariantCulture)); - - switch (SignatureAlgorithm) { - case DkimSignatureAlgorithm.Ed25519Sha256: - value.Append ("; a=ed25519-sha256"); - break; - case DkimSignatureAlgorithm.RsaSha256: - value.Append ("; a=rsa-sha256"); - break; - default: - value.Append ("; a=rsa-sha1"); - break; - } - - return value; - } - - Header GenerateArcMessageSignature (FormatOptions options, MimeMessage message, int instance, long t, IList headers) - { - var value = CreateArcHeaderBuilder (instance); - byte[] signature, hash; - Header ams; - - value.AppendFormat ("; d={0}; s={1}", Domain, Selector); - value.AppendFormat ("; c={0}/{1}", - HeaderCanonicalizationAlgorithm.ToString ().ToLowerInvariant (), - BodyCanonicalizationAlgorithm.ToString ().ToLowerInvariant ()); - value.AppendFormat ("; t={0}", t); - - using (var stream = new DkimSignatureStream (CreateSigningContext ())) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - // write the specified message headers - DkimVerifierBase.WriteHeaders (options, message, headers, HeaderCanonicalizationAlgorithm, filtered); - - value.AppendFormat ("; h={0}", string.Join (":", headers.ToArray ())); - - hash = message.HashBody (options, SignatureAlgorithm, BodyCanonicalizationAlgorithm, -1); - value.AppendFormat ("; bh={0}", Convert.ToBase64String (hash)); - value.Append ("; b="); - - ams = new Header (HeaderId.ArcMessageSignature, value.ToString ()); - - switch (HeaderCanonicalizationAlgorithm) { - case DkimCanonicalizationAlgorithm.Relaxed: - DkimVerifierBase.WriteHeaderRelaxed (options, filtered, ams, true); - break; - default: - DkimVerifierBase.WriteHeaderSimple (options, filtered, ams, true); - break; - } - - filtered.Flush (); - } - - signature = stream.GenerateSignature (); - - ams.Value += Convert.ToBase64String (signature); - - return ams; - } - } - - Header GenerateArcSeal (FormatOptions options, int instance, string cv, long t, ArcHeaderSet[] sets, int count, Header aar, Header ams) - { - var value = CreateArcHeaderBuilder (instance); - byte[] signature; - Header seal; - - value.AppendFormat ("; cv={0}", cv); - - value.AppendFormat ("; d={0}; s={1}", Domain, Selector); - value.AppendFormat ("; t={0}", t); - - using (var stream = new DkimSignatureStream (CreateSigningContext ())) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - for (int i = 0; i < count; i++) { - DkimVerifierBase.WriteHeaderRelaxed (options, filtered, sets[i].ArcAuthenticationResult, false); - DkimVerifierBase.WriteHeaderRelaxed (options, filtered, sets[i].ArcMessageSignature, false); - DkimVerifierBase.WriteHeaderRelaxed (options, filtered, sets[i].ArcSeal, false); - } - - DkimVerifierBase.WriteHeaderRelaxed (options, filtered, aar, false); - DkimVerifierBase.WriteHeaderRelaxed (options, filtered, ams, false); - - value.Append ("; b="); - - seal = new Header (HeaderId.ArcSeal, value.ToString ()); - DkimVerifierBase.WriteHeaderRelaxed (options, filtered, seal, true); - - filtered.Flush (); - } - - signature = stream.GenerateSignature (); - - seal.Value += Convert.ToBase64String (signature); - - return seal; - } - } - - async Task ArcSignAsync (FormatOptions options, MimeMessage message, IList headers, bool doAsync, CancellationToken cancellationToken) - { - ArcVerifier.GetArcHeaderSets (message, true, out ArcHeaderSet[] sets, out int count); - AuthenticationResults authres; - int instance = count + 1; - string cv; - - if (count > 0) { - var parameters = sets[count - 1].ArcSealParameters; - - // do not sign if there is already a failed ARC-Seal. - if (!parameters.TryGetValue ("cv", out cv) || cv.Equals ("fail", StringComparison.OrdinalIgnoreCase)) - return; - } - - options = options.Clone (); - options.NewLineFormat = NewLineFormat.Dos; - options.EnsureNewLine = true; - - if (doAsync) - authres = await GenerateArcAuthenticationResultsAsync (options, message, cancellationToken).ConfigureAwait (false); - else - authres = GenerateArcAuthenticationResults (options, message, cancellationToken); - - if (authres == null) - return; - - authres.Instance = instance; - - var aar = new Header (HeaderId.ArcAuthenticationResults, authres.ToString ()); - cv = "none"; - - if (count > 0) { - cv = "pass"; - - foreach (var method in authres.Results) { - if (method.Method.Equals ("arc", StringComparison.OrdinalIgnoreCase)) { - cv = method.Result; - break; - } - } - } - - var t = GetTimestamp (); - var ams = GenerateArcMessageSignature (options, message, instance, t, headers); - var seal = GenerateArcSeal (options, instance, cv, t, sets, count, aar, ams); - - message.Headers.Insert (0, aar); - message.Headers.Insert (0, ams); - message.Headers.Insert (0, seal); - } - - Task SignAsync (FormatOptions options, MimeMessage message, IList headers, bool doAsync, CancellationToken cancellationToken) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (headers == null) - throw new ArgumentNullException (nameof (headers)); - - var fields = new string[headers.Count]; - var containsFrom = false; - - for (int i = 0; i < headers.Count; i++) { - if (headers[i] == null) - throw new ArgumentException ("The list of headers cannot contain null.", nameof (headers)); - - if (headers[i].Length == 0) - throw new ArgumentException ("The list of headers cannot contain empty string.", nameof (headers)); - - fields[i] = headers[i].ToLowerInvariant (); - - if (ArcShouldNotInclude.Contains (fields[i])) - throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i]), nameof (headers)); - - if (fields[i] == "from") - containsFrom = true; - } - - if (!containsFrom) - throw new ArgumentException ("The list of headers to sign MUST include the 'From' header.", nameof (headers)); - - return ArcSignAsync (options, message, fields, doAsync, cancellationToken); - } - - Task SignAsync (FormatOptions options, MimeMessage message, IList headers, bool doAsync, CancellationToken cancellationToken) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (headers == null) - throw new ArgumentNullException (nameof (headers)); - - var fields = new string[headers.Count]; - var containsFrom = false; - - for (int i = 0; i < headers.Count; i++) { - if (headers[i] == HeaderId.Unknown) - throw new ArgumentException ("The list of headers to sign cannot include the 'Unknown' header.", nameof (headers)); - - fields[i] = headers[i].ToHeaderName ().ToLowerInvariant (); - - if (ArcShouldNotInclude.Contains (fields[i])) - throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i].ToHeaderName ()), nameof (headers)); - - if (headers[i] == HeaderId.From) - containsFrom = true; - } - - if (!containsFrom) - throw new ArgumentException ("The list of headers to sign MUST include the 'From' header.", nameof (headers)); - - return ArcSignAsync (options, message, fields, doAsync, cancellationToken); - } - - /// - /// Digitally sign and seal a message using ARC. - /// - /// - /// Digitally signs and seals a message using ARC. - /// - /// - /// - /// - /// The formatting options. - /// The message to sign. - /// The list of header fields to sign. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - /// - /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. - /// - public void Sign (FormatOptions options, MimeMessage message, IList headers, CancellationToken cancellationToken = default (CancellationToken)) - { - SignAsync (options, message, headers, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously digitally sign and seal a message using ARC. - /// - /// - /// Asynchronously digitally signs and seals a message using ARC. - /// - /// - /// - /// - /// An awaitable task. - /// The formatting options. - /// The message to sign. - /// The list of header fields to sign. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - /// - /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. - /// - public Task SignAsync (FormatOptions options, MimeMessage message, IList headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return SignAsync (options, message, headers, true, cancellationToken); - } - - /// - /// Digitally sign and seal a message using ARC. - /// - /// - /// Digitally signs and seals a message using ARC. - /// - /// - /// - /// - /// The message to sign. - /// The list of header fields to sign. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - /// - /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. - /// - public void Sign (MimeMessage message, IList headers, CancellationToken cancellationToken = default (CancellationToken)) - { - Sign (FormatOptions.Default, message, headers, cancellationToken); - } - - /// - /// Asynchronously digitally sign and seal a message using ARC. - /// - /// - /// Asynchronously digitally signs and seals a message using ARC. - /// - /// - /// - /// - /// An awaitable task. - /// The message to sign. - /// The list of header fields to sign. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - /// - /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. - /// - public Task SignAsync (MimeMessage message, IList headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return SignAsync (FormatOptions.Default, message, headers, cancellationToken); - } - - /// - /// Digitally sign and seal a message using ARC. - /// - /// - /// Digitally signs and seals a message using ARC. - /// - /// - /// - /// - /// The formatting options. - /// The message to sign. - /// The list of header fields to sign. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - /// - /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. - /// - public void Sign (FormatOptions options, MimeMessage message, IList headers, CancellationToken cancellationToken = default (CancellationToken)) - { - SignAsync (options, message, headers, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously digitally sign and seal a message using ARC. - /// - /// - /// Asynchronously digitally signs and seals a message using ARC. - /// - /// - /// - /// - /// An awaitable task. - /// The formatting options. - /// The message to sign. - /// The list of header fields to sign. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - /// - /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. - /// - public Task SignAsync (FormatOptions options, MimeMessage message, IList headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return SignAsync (options, message, headers, true, cancellationToken); - } - - /// - /// Digitally sign and seal a message using ARC. - /// - /// - /// Digitally signs and seals a message using ARC. - /// - /// - /// - /// - /// The message to sign. - /// The list of header fields to sign. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - /// - /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. - /// - public void Sign (MimeMessage message, IList headers, CancellationToken cancellationToken = default (CancellationToken)) - { - Sign (FormatOptions.Default, message, headers, cancellationToken); - } - - /// - /// Asynchronously digitally sign and seal a message using ARC. - /// - /// - /// Asynchronously digitally signs and seals a message using ARC. - /// - /// - /// - /// - /// An awaitable task. - /// The message to sign. - /// The list of header fields to sign. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - /// - /// One or more ARC headers either did not contain an instance tag or the instance tag was invalid. - /// - public Task SignAsync (MimeMessage message, IList headers, CancellationToken cancellationToken = default (CancellationToken)) - { - return SignAsync (FormatOptions.Default, message, headers, cancellationToken); - } - } -} diff --git a/src/MimeKit/Cryptography/ArcVerifier.cs b/src/MimeKit/Cryptography/ArcVerifier.cs deleted file mode 100644 index 982929f..0000000 --- a/src/MimeKit/Cryptography/ArcVerifier.cs +++ /dev/null @@ -1,694 +0,0 @@ -// -// ArcVerifier.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Parameters; - -using MimeKit.IO; - -namespace MimeKit.Cryptography { - /// - /// An ARC signature validation result. - /// - /// - /// An ARC signature validation result. - /// - /// - /// - /// - public enum ArcSignatureValidationResult - { - /// - /// No signatures to validate. - /// - None, - - /// - /// The validation passed. - /// - Pass, - - /// - /// The validation failed. - /// - Fail - } - - /// - /// An ARC header validation result. - /// - /// - /// Represents an ARC header and its signature validation result. - /// - /// - /// - /// - public class ArcHeaderValidationResult - { - /// - /// Initialize a new instance of the class. - /// - /// The ARC header. - /// - /// is null. - /// - internal ArcHeaderValidationResult (Header header) - { - if (header == null) - throw new ArgumentNullException (nameof (header)); - - Header = header; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The ARC header. - /// The signature validation result. - /// - /// is null. - /// - public ArcHeaderValidationResult (Header header, ArcSignatureValidationResult signature) : this (header) - { - Signature = signature; - } - - /// - /// Get the signature validation result. - /// - /// - /// Gets the signature validation result. - /// - /// The signature validation result. - public ArcSignatureValidationResult Signature { - get; internal set; - } - - /// - /// Get the ARC header. - /// - /// - /// Gets the ARC header. - /// - /// The ARC header. - public Header Header { - get; private set; - } - } - - /// - /// An ARC validation result. - /// - /// - /// Represents the results of ArcVerifier.Verify - /// or ArcVerifier.VerifyAsync. - /// If no ARC headers are found on the , then the result will be - /// and both and - /// will be null. - /// If ARC headers are found on the but could not be parsed, then the - /// result will be and both - /// and will be null. - /// - /// - /// - /// - public class ArcValidationResult - { - internal ArcValidationResult () - { - Chain = ArcSignatureValidationResult.None; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The signature validation results of the entire chain. - /// The validation results for the ARC-Message-Signature header. - /// The validation results for the ARC-Seal headers. - public ArcValidationResult (ArcSignatureValidationResult chain, ArcHeaderValidationResult messageSignature, ArcHeaderValidationResult[] seals) - { - MessageSignature = messageSignature; - Seals = seals; - Chain = chain; - } - - /// - /// Get the validation results for the ARC-Message-Signature header. - /// - /// - /// Gets the validation results for the ARC-Message-Signature header. - /// - /// The validation results for the ARC-Message-Signature header or null - /// if the ARC-Message-Signature header was not found. - public ArcHeaderValidationResult MessageSignature { - get; internal set; - } - - /// - /// Get the validation results for each of the ARC-Seal headers. - /// - /// - /// Gets the validation results for each of the ARC-Seal headers in - /// their instance order. - /// - /// The array of validation results for the ARC-Seal headers or null - /// if no ARC-Seal headers were found. - public ArcHeaderValidationResult[] Seals { - get; internal set; - } - - /// - /// Get the signature validation results of the entire chain. - /// - /// - /// Gets the signature validation results of the entire chain. - /// - /// - /// - /// - /// The signature validation results of the entire chain. - public ArcSignatureValidationResult Chain { - get; internal set; - } - } - - class ArcHeaderSet - { - public Header ArcAuthenticationResult { get; private set; } - - public Dictionary ArcMessageSignatureParameters { get; private set; } - public Header ArcMessageSignature { get; private set; } - - public Dictionary ArcSealParameters { get; private set; } - public Header ArcSeal { get; private set; } - - public bool Add (Header header, Dictionary parameters) - { - switch (header.Id) { - case HeaderId.ArcAuthenticationResults: - if (ArcAuthenticationResult != null) - return false; - - ArcAuthenticationResult = header; - break; - case HeaderId.ArcMessageSignature: - if (ArcMessageSignature != null) - return false; - - ArcMessageSignatureParameters = parameters; - ArcMessageSignature = header; - break; - case HeaderId.ArcSeal: - if (ArcSeal != null) - return false; - - ArcSealParameters = parameters; - ArcSeal = header; - break; - default: - return false; - } - - return true; - } - } - - /// - /// An ARC verifier. - /// - /// - /// Validates Authenticated Received Chains. - /// - /// - /// - /// - public class ArcVerifier : DkimVerifierBase - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// - /// - /// - /// The public key locator. - /// - /// is null. - /// - public ArcVerifier (IDkimPublicKeyLocator publicKeyLocator) : base (publicKeyLocator) - { - } - - static void ValidateArcMessageSignatureParameters (IDictionary parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, - out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) - { - ValidateCommonSignatureParameters ("ARC-Message-Signature", parameters, out algorithm, out headerAlgorithm, out bodyAlgorithm, out d, out s, out q, out headers, out bh, out b, out maxLength); - } - - static void ValidateArcSealParameters (IDictionary parameters, out DkimSignatureAlgorithm algorithm, out string d, out string s, out string q, out string b) - { - ValidateCommonParameters ("ARC-Seal", parameters, out algorithm, out d, out s, out q, out b); - - if (parameters.TryGetValue ("h", out string h)) - throw new FormatException (string.Format ("Malformed ARC-Seal header: the 'h' parameter tag is not allowed.")); - } - - async Task VerifyArcMessageSignatureAsync (FormatOptions options, MimeMessage message, Header arcSignature, Dictionary parameters, bool doAsync, CancellationToken cancellationToken) - { - DkimCanonicalizationAlgorithm headerAlgorithm, bodyAlgorithm; - DkimSignatureAlgorithm signatureAlgorithm; - AsymmetricKeyParameter key; - string d, s, q, bh, b; - string[] headers; - int maxLength; - - ValidateArcMessageSignatureParameters (parameters, out signatureAlgorithm, out headerAlgorithm, out bodyAlgorithm, - out d, out s, out q, out headers, out bh, out b, out maxLength); - - if (!IsEnabled (signatureAlgorithm)) - return false; - - if (doAsync) - key = await PublicKeyLocator.LocatePublicKeyAsync (q, d, s, cancellationToken).ConfigureAwait (false); - else - key = PublicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); - - if ((key is RsaKeyParameters rsa) && rsa.Modulus.BitLength < MinimumRsaKeyLength) - return false; - - options = options.Clone (); - options.NewLineFormat = NewLineFormat.Dos; - - // first check the body hash (if that's invalid, then the entire signature is invalid) - var hash = Convert.ToBase64String (message.HashBody (options, signatureAlgorithm, bodyAlgorithm, maxLength)); - - if (hash != bh) - return false; - - using (var stream = new DkimSignatureStream (CreateVerifyContext (signatureAlgorithm, key))) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - WriteHeaders (options, message, headers, headerAlgorithm, filtered); - - // now include the ARC-Message-Signature header that we are verifying, - // but only after removing the "b=" signature value. - var header = GetSignedSignatureHeader (arcSignature); - - switch (headerAlgorithm) { - case DkimCanonicalizationAlgorithm.Relaxed: - WriteHeaderRelaxed (options, filtered, header, true); - break; - default: - WriteHeaderSimple (options, filtered, header, true); - break; - } - - filtered.Flush (); - } - - return stream.VerifySignature (b); - } - } - - async Task VerifyArcSealAsync (FormatOptions options, ArcHeaderSet[] sets, int i, bool doAsync, CancellationToken cancellationToken) - { - DkimSignatureAlgorithm algorithm; - AsymmetricKeyParameter key; - string d, s, q, b; - - ValidateArcSealParameters (sets[i].ArcSealParameters, out algorithm, out d, out s, out q, out b); - - if (!IsEnabled (algorithm)) - return false; - - if (doAsync) - key = await PublicKeyLocator.LocatePublicKeyAsync (q, d, s, cancellationToken).ConfigureAwait (false); - else - key = PublicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); - - if ((key is RsaKeyParameters rsa) && rsa.Modulus.BitLength < MinimumRsaKeyLength) - return false; - - options = options.Clone (); - options.NewLineFormat = NewLineFormat.Dos; - - using (var stream = new DkimSignatureStream (CreateVerifyContext (algorithm, key))) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - for (int j = 0; j < i; j++) { - WriteHeaderRelaxed (options, filtered, sets[j].ArcAuthenticationResult, false); - WriteHeaderRelaxed (options, filtered, sets[j].ArcMessageSignature, false); - WriteHeaderRelaxed (options, filtered, sets[j].ArcSeal, false); - } - - WriteHeaderRelaxed (options, filtered, sets[i].ArcAuthenticationResult, false); - WriteHeaderRelaxed (options, filtered, sets[i].ArcMessageSignature, false); - - // now include the ARC-Seal header that we are verifying, - // but only after removing the "b=" signature value. - var seal = GetSignedSignatureHeader (sets[i].ArcSeal); - - WriteHeaderRelaxed (options, filtered, seal, true); - - filtered.Flush (); - } - - return stream.VerifySignature (b); - } - } - - internal static ArcSignatureValidationResult GetArcHeaderSets (MimeMessage message, bool throwOnError, out ArcHeaderSet[] sets, out int count) - { - ArcHeaderSet set; - - sets = new ArcHeaderSet[50]; - count = 0; - - for (int i = 0; i < message.Headers.Count; i++) { - Dictionary parameters = null; - var header = message.Headers[i]; - int instance; - string value; - - switch (header.Id) { - case HeaderId.ArcAuthenticationResults: - if (!AuthenticationResults.TryParse (header.RawValue, out AuthenticationResults authres)) { - if (throwOnError) - throw new FormatException ("Invalid ARC-Authentication-Results header."); - - return ArcSignatureValidationResult.Fail; - } - - if (!authres.Instance.HasValue) { - if (throwOnError) - throw new FormatException ("Missing instance tag in ARC-Authentication-Results header."); - - return ArcSignatureValidationResult.Fail; - } - - instance = authres.Instance.Value; - - if (instance < 1 || instance > 50) { - if (throwOnError) - throw new FormatException (string.Format ("Invalid instance tag in ARC-Authentication-Results header: i={0}", instance)); - - return ArcSignatureValidationResult.Fail; - } - break; - case HeaderId.ArcMessageSignature: - case HeaderId.ArcSeal: - try { - parameters = ParseParameterTags (header.Id, header.Value); - } catch { - if (throwOnError) - throw; - - return ArcSignatureValidationResult.Fail; - } - - if (!parameters.TryGetValue ("i", out value)) { - if (throwOnError) - throw new FormatException (string.Format ("Missing instance tag in {0} header.", header.Id.ToHeaderName ())); - - return ArcSignatureValidationResult.Fail; - } - - if (!int.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out instance) || instance < 1 || instance > 50) { - if (throwOnError) - throw new FormatException (string.Format ("Invalid instance tag in {0} header: i={1}", header.Id.ToHeaderName (), value)); - - return ArcSignatureValidationResult.Fail; - } - break; - default: - instance = 0; - break; - } - - if (instance == 0) - continue; - - set = sets[instance - 1]; - if (set == null) - sets[instance - 1] = set = new ArcHeaderSet (); - - if (!set.Add (header, parameters)) - return ArcSignatureValidationResult.Fail; - - if (instance > count) - count = instance; - } - - if (count == 0) { - // there are no ARC sets - return ArcSignatureValidationResult.None; - } - - // verify that all ARC sets are complete - for (int i = 0; i < count; i++) { - set = sets[i]; - - if (set == null) { - if (throwOnError) - throw new FormatException (string.Format ("Missing ARC headers for i={0}", i + 1)); - - return ArcSignatureValidationResult.Fail; - } - - if (set.ArcAuthenticationResult == null) { - if (throwOnError) - throw new FormatException (string.Format ("Missing ARC-Authentication-Results header for i={0}", i + 1)); - - return ArcSignatureValidationResult.Fail; - } - - if (set.ArcMessageSignature == null) { - if (throwOnError) - throw new FormatException (string.Format ("Missing ARC-Message-Signature header for i={0}", i + 1)); - - return ArcSignatureValidationResult.Fail; - } - - if (set.ArcSeal == null) { - if (throwOnError) - throw new FormatException (string.Format ("Missing ARC-Seal header for i={0}", i + 1)); - - return ArcSignatureValidationResult.Fail; - } - - if (!set.ArcSealParameters.TryGetValue ("cv", out string cv)) { - if (throwOnError) - throw new FormatException (string.Format ("Missing chain validation tag in ARC-Seal header for i={0}.", i + 1)); - - return ArcSignatureValidationResult.Fail; - } - - // The "cv" value for all ARC-Seal header fields MUST NOT be - // "fail". For ARC Sets with instance values > 1, the values - // MUST be "pass". For the ARC Set with instance value = 1, the - // value MUST be "none". - if (!cv.Equals (i == 0 ? "none" : "pass", StringComparison.Ordinal)) - return ArcSignatureValidationResult.Fail; - } - - return ArcSignatureValidationResult.Pass; - } - - async Task VerifyAsync (FormatOptions options, MimeMessage message, bool doAsync, CancellationToken cancellationToken) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - var result = new ArcValidationResult (); - - switch (GetArcHeaderSets (message, false, out ArcHeaderSet[] sets, out int count)) { - case ArcSignatureValidationResult.None: return result; - case ArcSignatureValidationResult.Fail: - result.Chain = ArcSignatureValidationResult.Fail; - return result; - } - - int newest = count - 1; - - result.Seals = new ArcHeaderValidationResult[count]; - result.Chain = ArcSignatureValidationResult.Pass; - - // validate the most recent Arc-Message-Signature - try { - var parameters = sets[newest].ArcMessageSignatureParameters; - var header = sets[newest].ArcMessageSignature; - - result.MessageSignature = new ArcHeaderValidationResult (header); - - if (await VerifyArcMessageSignatureAsync (options, message, header, parameters, doAsync, cancellationToken).ConfigureAwait (false)) { - result.MessageSignature.Signature = ArcSignatureValidationResult.Pass; - } else { - result.MessageSignature.Signature = ArcSignatureValidationResult.Fail; - result.Chain = ArcSignatureValidationResult.Fail; - } - } catch { - result.MessageSignature.Signature = ArcSignatureValidationResult.Fail; - result.Chain = ArcSignatureValidationResult.Fail; - } - - // validate all Arc-Seals starting with the most recent and proceeding to the oldest - for (int i = newest; i >= 0; i--) { - result.Seals[i] = new ArcHeaderValidationResult (sets[i].ArcSeal); - - try { - if (await VerifyArcSealAsync (options, sets, i, doAsync, cancellationToken).ConfigureAwait (false)) { - result.Seals[i].Signature = ArcSignatureValidationResult.Pass; - } else { - result.Seals[i].Signature = ArcSignatureValidationResult.Fail; - result.Chain = ArcSignatureValidationResult.Fail; - } - } catch { - result.Seals[i].Signature = ArcSignatureValidationResult.Fail; - result.Chain = ArcSignatureValidationResult.Fail; - } - } - - return result; - } - - /// - /// Verify the ARC signature chain. - /// - /// - /// Verifies the ARC signature chain. - /// - /// - /// - /// - /// The ARC validation result. - /// The formatting options. - /// The message to verify. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public ArcValidationResult Verify (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (options, message, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously verify the ARC signature chain. - /// - /// - /// Asynchronously verifies the ARC signature chain. - /// - /// - /// - /// - /// The ARC validation result. - /// The formatting options. - /// The message to verify. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public Task VerifyAsync (FormatOptions options, MimeMessage message, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (options, message, true, cancellationToken); - } - - /// - /// Verify the ARC signature chain. - /// - /// - /// Verifies the ARC signature chain. - /// - /// - /// - /// - /// The ARC validation result. - /// The message to verify. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public ArcValidationResult Verify (MimeMessage message, CancellationToken cancellationToken = default (CancellationToken)) - { - return Verify (FormatOptions.Default, message, cancellationToken); - } - - /// - /// Asynchronously verify the ARC signature chain. - /// - /// - /// Asynchronously verifies the ARC signature chain. - /// - /// - /// - /// - /// The ARC validation result. - /// The message to verify. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public Task VerifyAsync (MimeMessage message, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (FormatOptions.Default, message, cancellationToken); - } - } -} diff --git a/src/MimeKit/Cryptography/AsymmetricAlgorithmExtensions.cs b/src/MimeKit/Cryptography/AsymmetricAlgorithmExtensions.cs deleted file mode 100644 index a422250..0000000 --- a/src/MimeKit/Cryptography/AsymmetricAlgorithmExtensions.cs +++ /dev/null @@ -1,373 +0,0 @@ -// -// AsymmetricAlgorithmExtensions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Security.Cryptography; - -using Org.BouncyCastle.Math; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Parameters; - -namespace MimeKit.Cryptography -{ - /// - /// Extension methods for System.Security.Cryptography.AsymmetricAlgorithm. - /// - /// - /// Extension methods for System.Security.Cryptography.AsymmetricAlgorithm. - /// - public static class AsymmetricAlgorithmExtensions - { - static void GetAsymmetricKeyParameters (DSA dsa, bool publicOnly, out AsymmetricKeyParameter pub, out AsymmetricKeyParameter key) - { - var dp = dsa.ExportParameters (!publicOnly); - var validationParameters = dp.Seed != null ? new DsaValidationParameters (dp.Seed, dp.Counter) : null; - var parameters = new DsaParameters ( - new BigInteger (1, dp.P), - new BigInteger (1, dp.Q), - new BigInteger (1, dp.G), - validationParameters); - - pub = new DsaPublicKeyParameters (new BigInteger (1, dp.Y), parameters); - key = publicOnly ? null : new DsaPrivateKeyParameters (new BigInteger (1, dp.X), parameters); - } - - static AsymmetricKeyParameter GetAsymmetricKeyParameter (DSACryptoServiceProvider dsa) - { - GetAsymmetricKeyParameters (dsa, dsa.PublicOnly, out var pub, out var key); - - return dsa.PublicOnly ? pub : key; - } - - static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair (DSACryptoServiceProvider dsa) - { - if (dsa.PublicOnly) - throw new ArgumentException ("DSA key is not a private key.", "key"); - - GetAsymmetricKeyParameters (dsa, dsa.PublicOnly, out var pub, out var key); - - return new AsymmetricCipherKeyPair (pub, key); - } - - static AsymmetricKeyParameter GetAsymmetricKeyParameter (DSA dsa) - { - GetAsymmetricKeyParameters (dsa, false, out _, out var key); - - return key; - } - - static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair (DSA dsa) - { - GetAsymmetricKeyParameters (dsa, false, out var pub, out var key); - - return new AsymmetricCipherKeyPair (pub, key); - } - - static void GetAsymmetricKeyParameters (RSA rsa, bool publicOnly, out AsymmetricKeyParameter pub, out AsymmetricKeyParameter key) - { - var rp = rsa.ExportParameters (!publicOnly); - var modulus = new BigInteger (1, rp.Modulus); - var exponent = new BigInteger (1, rp.Exponent); - - pub = new RsaKeyParameters (false, modulus, exponent); - key = publicOnly ? null : new RsaPrivateCrtKeyParameters ( - modulus, - exponent, - new BigInteger (1, rp.D), - new BigInteger (1, rp.P), - new BigInteger (1, rp.Q), - new BigInteger (1, rp.DP), - new BigInteger (1, rp.DQ), - new BigInteger (1, rp.InverseQ) - ); - } - - static AsymmetricKeyParameter GetAsymmetricKeyParameter (RSACryptoServiceProvider rsa) - { - GetAsymmetricKeyParameters (rsa, rsa.PublicOnly, out var pub, out var key); - - return rsa.PublicOnly ? pub : key; - } - - static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair (RSACryptoServiceProvider rsa) - { - if (rsa.PublicOnly) - throw new ArgumentException ("RSA key is not a private key.", "key"); - - GetAsymmetricKeyParameters (rsa, rsa.PublicOnly, out var pub, out var key); - - return new AsymmetricCipherKeyPair (pub, key); - } - - static AsymmetricKeyParameter GetAsymmetricKeyParameter (RSA rsa) - { - GetAsymmetricKeyParameters (rsa, false, out _, out var key); - - return key; - } - - static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair (RSA rsa) - { - GetAsymmetricKeyParameters (rsa, false, out var pub, out var key); - - return new AsymmetricCipherKeyPair (pub, key); - } - - /// - /// Convert an AsymmetricAlgorithm into a BouncyCastle AsymmetricKeyParameter. - /// - /// - /// Converts an AsymmetricAlgorithm into a BouncyCastle AsymmetricKeyParameter. - /// Currently, only RSA and DSA keys are supported. - /// - /// The Bouncy Castle AsymmetricKeyParameter. - /// The key. - /// - /// is null. - /// - /// - /// is an unsupported asymmetric algorithm. - /// - public static AsymmetricKeyParameter AsAsymmetricKeyParameter (this AsymmetricAlgorithm key) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (key is RSACryptoServiceProvider rsaKey) - return GetAsymmetricKeyParameter (rsaKey); - - if (key is RSA rsa) - return GetAsymmetricKeyParameter (rsa); - - if (key is DSACryptoServiceProvider dsaKey) - return GetAsymmetricKeyParameter (dsaKey); - - if (key is DSA dsa) - return GetAsymmetricKeyParameter (dsa); - - // TODO: support ECDiffieHellman and ECDsa? - - throw new NotSupportedException (string.Format ("'{0}' is currently not supported.", key.GetType ().Name)); - } - - /// - /// Convert an AsymmetricAlgorithm into a BouncyCastle AsymmetricCipherKeyPair. - /// - /// - /// Converts an AsymmetricAlgorithm into a BouncyCastle AsymmetricCipherKeyPair. - /// Currently, only RSA and DSA keys are supported. - /// - /// The Bouncy Castle AsymmetricCipherKeyPair. - /// The key. - /// - /// is null. - /// - /// - /// is a public key. - /// - /// - /// is an unsupported asymmetric algorithm. - /// - public static AsymmetricCipherKeyPair AsAsymmetricCipherKeyPair (this AsymmetricAlgorithm key) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (key is RSACryptoServiceProvider rsaKey) - return GetAsymmetricCipherKeyPair (rsaKey); - - if (key is RSA rsa) - return GetAsymmetricCipherKeyPair (rsa); - - if (key is DSACryptoServiceProvider dsaKey) - return GetAsymmetricCipherKeyPair (dsaKey); - - if (key is DSA dsa) - return GetAsymmetricCipherKeyPair (dsa); - - // TODO: support ECDiffieHellman and ECDsa? - - throw new NotSupportedException (string.Format ("'{0}' is currently not supported.", key.GetType ().Name)); - } - - static byte[] GetPaddedByteArray (BigInteger big, int length) - { - var bytes = big.ToByteArrayUnsigned (); - - if (bytes.Length >= length) - return bytes; - - var padded = new byte[length]; - - Buffer.BlockCopy (bytes, 0, padded, length - bytes.Length, bytes.Length); - - return padded; - } - - static DSAParameters GetDSAParameters (DsaKeyParameters key) - { - var parameters = new DSAParameters (); - - if (key.Parameters.ValidationParameters != null) { - parameters.Counter = key.Parameters.ValidationParameters.Counter; - parameters.Seed = key.Parameters.ValidationParameters.GetSeed (); - } - - parameters.G = key.Parameters.G.ToByteArrayUnsigned (); - parameters.P = key.Parameters.P.ToByteArrayUnsigned (); - parameters.Q = key.Parameters.Q.ToByteArrayUnsigned (); - - return parameters; - } - - static AsymmetricAlgorithm GetAsymmetricAlgorithm (DsaPrivateKeyParameters key, DsaPublicKeyParameters pub) - { - var parameters = GetDSAParameters (key); - parameters.X = key.X.ToByteArrayUnsigned (); - - if (pub != null) - parameters.Y = pub.Y.ToByteArrayUnsigned (); - - var dsa = new DSACryptoServiceProvider (); - - dsa.ImportParameters (parameters); - - return dsa; - } - - static AsymmetricAlgorithm GetAsymmetricAlgorithm (DsaPublicKeyParameters key) - { - var parameters = GetDSAParameters (key); - parameters.Y = key.Y.ToByteArrayUnsigned (); - - var dsa = new DSACryptoServiceProvider (); - - dsa.ImportParameters (parameters); - - return dsa; - } - - static AsymmetricAlgorithm GetAsymmetricAlgorithm (RsaPrivateCrtKeyParameters key) - { - var parameters = new RSAParameters (); - - parameters.Exponent = key.PublicExponent.ToByteArrayUnsigned (); - parameters.Modulus = key.Modulus.ToByteArrayUnsigned (); - parameters.P = key.P.ToByteArrayUnsigned (); - parameters.Q = key.Q.ToByteArrayUnsigned (); - - parameters.InverseQ = GetPaddedByteArray (key.QInv, parameters.Q.Length); - parameters.D = GetPaddedByteArray (key.Exponent, parameters.Modulus.Length); - parameters.DP = GetPaddedByteArray (key.DP, parameters.P.Length); - parameters.DQ = GetPaddedByteArray (key.DQ, parameters.Q.Length); - - var rsa = new RSACryptoServiceProvider (); - - rsa.ImportParameters (parameters); - - return rsa; - } - - static AsymmetricAlgorithm GetAsymmetricAlgorithm (RsaKeyParameters key) - { - var parameters = new RSAParameters (); - parameters.Exponent = key.Exponent.ToByteArrayUnsigned (); - parameters.Modulus = key.Modulus.ToByteArrayUnsigned (); - - var rsa = new RSACryptoServiceProvider (); - - rsa.ImportParameters (parameters); - - return rsa; - } - - /// - /// Convert a BouncyCastle AsymmetricKeyParameter into an AsymmetricAlgorithm. - /// - /// - /// Converts a BouncyCastle AsymmetricKeyParameter into an AsymmetricAlgorithm. - /// Currently, only RSA and DSA keys are supported. - /// - /// The AsymmetricAlgorithm. - /// The AsymmetricKeyParameter. - /// - /// is null. - /// - /// - /// is an unsupported asymmetric key parameter. - /// - public static AsymmetricAlgorithm AsAsymmetricAlgorithm (this AsymmetricKeyParameter key) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (key.IsPrivate) { - if (key is RsaPrivateCrtKeyParameters rsaPrivateKey) - return GetAsymmetricAlgorithm (rsaPrivateKey); - - if (key is DsaPrivateKeyParameters dsaPrivateKey) - return GetAsymmetricAlgorithm (dsaPrivateKey, null); - } else { - if (key is RsaKeyParameters rsaPublicKey) - return GetAsymmetricAlgorithm (rsaPublicKey); - - if (key is DsaPublicKeyParameters dsaPublicKey) - return GetAsymmetricAlgorithm (dsaPublicKey); - } - - throw new NotSupportedException (string.Format ("{0} is currently not supported.", key.GetType ().Name)); - } - - /// - /// Convert a BouncyCastle AsymmetricCipherKeyPair into an AsymmetricAlgorithm. - /// - /// - /// Converts a BouncyCastle AsymmetricCipherKeyPair into an AsymmetricAlgorithm. - /// Currently, only RSA and DSA keys are supported. - /// - /// The AsymmetricAlgorithm. - /// The AsymmetricCipherKeyPair. - /// - /// is null. - /// - /// - /// is an unsupported asymmetric algorithm. - /// - public static AsymmetricAlgorithm AsAsymmetricAlgorithm (this AsymmetricCipherKeyPair key) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (key.Private is RsaPrivateCrtKeyParameters rsaPrivateKey) - return GetAsymmetricAlgorithm (rsaPrivateKey); - - if (key.Private is DsaPrivateKeyParameters dsaPrivateKey) - return GetAsymmetricAlgorithm (dsaPrivateKey, (DsaPublicKeyParameters) key.Public); - - throw new NotSupportedException (string.Format ("{0} is currently not supported.", key.GetType ().Name)); - } - } -} diff --git a/src/MimeKit/Cryptography/AuthenticationResults.cs b/src/MimeKit/Cryptography/AuthenticationResults.cs deleted file mode 100644 index f2ea9c1..0000000 --- a/src/MimeKit/Cryptography/AuthenticationResults.cs +++ /dev/null @@ -1,1366 +0,0 @@ -// -// AuthenticationResults.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Globalization; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit.Cryptography { - /// - /// A parsed representation of the Authentication-Results header. - /// - /// - /// The Authentication-Results header is used with electronic mail messages to - /// indicate the results of message authentication efforts. Any receiver-side - /// software, such as mail filters or Mail User Agents (MUAs), can use this header - /// field to relay that information in a convenient and meaningful way to users or - /// to make sorting and filtering decisions. - /// - public class AuthenticationResults - { - AuthenticationResults () - { - Results = new List (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The authentication service identifier. - /// - /// is null. - /// - public AuthenticationResults (string authservid) : this () - { - if (authservid == null) - throw new ArgumentNullException (nameof (authservid)); - - AuthenticationServiceIdentifier = authservid; - } - - /// - /// Get the authentication service identifier. - /// - /// - /// Gets the authentication service identifier. - /// The authentication service identifier is the authserv-id token - /// as defined in rfc7601. - /// - /// The authserv-id token. - public string AuthenticationServiceIdentifier { - get; private set; - } - - /// - /// Get or set the instance value. - /// - /// - /// Gets or sets the instance value. - /// This value will only be set if the - /// represents an ARC-Authentication-Results header value. - /// - /// The instance. - public int? Instance { - get; set; - } - - /// - /// Get or set the Authentication-Results version. - /// - /// - /// Gets or sets the Authentication-Results version. - /// The version value is the authres-version token as defined in - /// rfc7601. - /// - /// The authres-version token. - public int? Version { - get; set; - } - - /// - /// Get the list of authentication results. - /// - /// - /// Gets the list of authentication results. - /// - /// The list of authentication results. - public List Results { - get; private set; - } - - internal void Encode (FormatOptions options, StringBuilder builder, int lineLength) - { - int space = 1; - - if (Instance.HasValue) { - var i = Instance.Value.ToString (CultureInfo.InvariantCulture); - - builder.AppendFormat (" i={0};", i); - lineLength += 4 + i.Length; - } - - if (AuthenticationServiceIdentifier != null) { - if (lineLength + space + AuthenticationServiceIdentifier.Length > options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - space = 0; - } - - if (space > 0) { - builder.Append (' '); - lineLength++; - } - - builder.Append (AuthenticationServiceIdentifier); - lineLength += AuthenticationServiceIdentifier.Length; - - if (Version.HasValue) { - var version = Version.Value.ToString (CultureInfo.InvariantCulture); - - if (lineLength + 1 + version.Length > options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - } else { - builder.Append (' '); - lineLength++; - } - - lineLength += version.Length; - builder.Append (version); - } - - builder.Append (';'); - lineLength++; - } - - if (Results.Count > 0) { - for (int i = 0; i < Results.Count; i++) { - if (i > 0) { - builder.Append (';'); - lineLength++; - } - - Results[i].Encode (options, builder, ref lineLength); - } - } else { - builder.Append (" none"); - } - - builder.Append (options.NewLine); - } - - /// - /// Serializes the to a string. - /// - /// - /// Creates a string-representation of the . - /// - /// The serialized string. - public override string ToString () - { - var builder = new StringBuilder (); - - if (Instance.HasValue) - builder.AppendFormat ("i={0}; ", Instance.Value.ToString (CultureInfo.InvariantCulture)); - - if (AuthenticationServiceIdentifier != null) { - builder.Append (AuthenticationServiceIdentifier); - - if (Version.HasValue) { - builder.Append (' '); - builder.Append (Version.Value.ToString (CultureInfo.InvariantCulture)); - } - - builder.Append ("; "); - } - - if (Results.Count > 0) { - for (int i = 0; i < Results.Count; i++) { - if (i > 0) - builder.Append ("; "); - builder.Append (Results[i]); - } - } else { - builder.Append ("none"); - } - - return builder.ToString (); - } - - static bool IsKeyword (byte c) - { - return (c >= (byte) 'A' && c <= (byte) 'Z') || - (c >= (byte) 'a' && c <= (byte) 'z') || - (c >= (byte) '0' && c <= (byte) '9') || - c == (byte) '-'; - } - - static bool SkipKeyword (byte[] text, ref int index, int endIndex) - { - int startIndex = index; - - while (index < endIndex && IsKeyword (text[index])) - index++; - - return index > startIndex; - } - - static bool SkipValue (byte[] text, ref int index, int endIndex, out bool quoted) - { - if (text[index] == (byte) '"') { - quoted = true; - - if (!ParseUtils.SkipQuoted (text, ref index, endIndex, false)) - return false; - } else { - quoted = false; - - if (!ParseUtils.SkipToken (text, ref index, endIndex)) - return false; - } - - return true; - } - - static bool SkipDomain (byte[] text, ref int index, int endIndex) - { - int startIndex = index; - - while (ParseUtils.SkipAtom (text, ref index, endIndex) && index < endIndex && text[index] == (byte) '.') - index++; - - if (index > startIndex && text[index - 1] != (byte) '.') - return true; - - return false; - } - - // pvalue := [CFWS] ( value / [ [ local-part ] "@" ] domain-name ) [CFWS] - // value := token / quoted-string - // token := 1* - // tspecials := "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "=" - static bool IsPValueToken (byte c) - { - // Note: We're allowing '/' because it is a base64 character - // - // See https://github.com/jstedfast/MimeKit/issues/518 for details. - return c.IsToken () || c == (byte) '/'; - } - - static void SkipPValueToken (byte[] text, ref int index, int endIndex) - { - while (index < endIndex && IsPValueToken (text[index])) - index++; - } - - static bool SkipPropertyValue (byte[] text, ref int index, int endIndex, out bool quoted) - { - // pvalue := [CFWS] ( value / [ [ local-part ] "@" ] domain-name ) [CFWS] - // value := token / quoted-string - // token := 1* - // tspecials := "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "=" - if (text[index] == (byte) '"') { - // quoted-string - quoted = true; - - if (!ParseUtils.SkipQuoted (text, ref index, endIndex, false)) - return false; - - return true; - } - - quoted = false; - - if (text[index] == (byte) '@') { - // "@" domain-name - index++; - - if (!SkipDomain (text, ref index, endIndex)) - return false; - - return true; - } - - SkipPValueToken (text, ref index, endIndex); - - if (index < endIndex) { - if (text[index] == (byte) '@') { - // local-part@domain-name - index++; - - if (!SkipDomain (text, ref index, endIndex)) - return false; - } - } - - return true; - } - - static bool TryParseMethods (byte[] text, ref int index, int endIndex, bool throwOnError, AuthenticationResults authres) - { - string value; - bool quoted; - - while (index < endIndex) { - string srvid = null; - - method_token: - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) - break; - - int methodIndex = index; - - // skip the method name - if (!SkipKeyword (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid method token at offset {0}", methodIndex), methodIndex, index); - - return false; - } - - // Note: Office365 seems to (sometimes) place a method-specific authserv-id token before each - // method. This block of code is here to handle that case. - // - // See https://github.com/jstedfast/MimeKit/issues/527 for details. - if (srvid == null && index < endIndex && text[index] == '.') { - index = methodIndex; - - if (!SkipDomain (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid Office365 authserv-id token at offset {0}", methodIndex), methodIndex, index); - - return false; - } - - srvid = Encoding.UTF8.GetString (text, methodIndex, index - methodIndex); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Missing semi-colon after Office365 authserv-id token at offset {0}", methodIndex), methodIndex, index); - - return false; - } - - if (text[index] != (byte) ';') { - if (throwOnError) - throw new ParseException (string.Format ("Unexpected token after Office365 authserv-id token at offset {0}", index), index, index); - - return false; - } - - // skip over ';' - index++; - - goto method_token; - } - - var method = Encoding.ASCII.GetString (text, methodIndex, index - methodIndex); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (method != "none") { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); - - return false; - } - - if (authres.Results.Count > 0) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid no-result token at offset {0}", methodIndex), methodIndex, index); - - return false; - } - - break; - } - - var resinfo = new AuthenticationMethodResult (method); - resinfo.Office365AuthenticationServiceIdentifier = srvid; - authres.Results.Add (resinfo); - - int tokenIndex; - - if (text[index] == (byte) '/') { - // optional method-version token - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - tokenIndex = index; - - if (!ParseUtils.TryParseInt32 (text, ref index, endIndex, out int version)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid method-version token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - resinfo.Version = version; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); - - return false; - } - } - - if (text[index] != (byte) '=') { - if (throwOnError) - throw new ParseException (string.Format ("Invalid methodspec token at offset {0}", methodIndex), methodIndex, index); - - return false; - } - - // skip over '=' - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete methodspec token at offset {0}", methodIndex), methodIndex, index); - - return false; - } - - tokenIndex = index; - - if (!SkipKeyword (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid result token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - resinfo.Result = Encoding.ASCII.GetString (text, tokenIndex, index - tokenIndex); - - ParseUtils.SkipWhiteSpace (text, ref index, endIndex); - - if (index < endIndex && text[index] == (byte) '(') { - int commentIndex = index; - - if (!ParseUtils.SkipComment (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete comment token at offset {0}", commentIndex), commentIndex, index); - - return false; - } - - commentIndex++; - - resinfo.ResultComment = Header.Unfold (Encoding.UTF8.GetString (text, commentIndex, (index - 1) - commentIndex)); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - } - - if (index >= endIndex) - break; - - if (text[index] == (byte) ';') { - index++; - continue; - } - - // optional reasonspec or propspec - tokenIndex = index; - - if (!SkipKeyword (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid reasonspec or propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - value = Encoding.ASCII.GetString (text, tokenIndex, index - tokenIndex); - - if (value == "reason" || value == "action") { - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete {0}spec token at offset {1}", value, tokenIndex), tokenIndex, index); - - return false; - } - - if (text[index] != (byte) '=') { - if (throwOnError) - throw new ParseException (string.Format ("Invalid {0}spec token at offset {1}", value, tokenIndex), tokenIndex, index); - - return false; - } - - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - int reasonIndex = index; - - if (index >= endIndex || !SkipValue (text, ref index, endIndex, out quoted)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid {0}spec value token at offset {1}", value, reasonIndex), reasonIndex, index); - - return false; - } - - var reason = Encoding.UTF8.GetString (text, reasonIndex, index - reasonIndex); - - if (quoted) - reason = MimeUtils.Unquote (reason); - - if (value == "action") - resinfo.Action = reason; - else - resinfo.Reason = reason; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) - break; - - if (text[index] == (byte) ';') { - index++; - continue; - } - - // optional propspec - tokenIndex = index; - - if (!SkipKeyword (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - value = Encoding.ASCII.GetString (text, tokenIndex, index - tokenIndex); - } - - do { - // value is a propspec ptype token - var ptype = value; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - if (text[index] != (byte) '.') { - if (throwOnError) - throw new ParseException (string.Format ("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - int propertyIndex = index; - - if (!SkipKeyword (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid property token at offset {0}", propertyIndex), propertyIndex, index); - - return false; - } - - var property = Encoding.ASCII.GetString (text, propertyIndex, index - propertyIndex); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - if (text[index] != (byte) '=') { - if (throwOnError) - throw new ParseException (string.Format ("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - int valueIndex = index; - - if (index >= text.Length || !SkipPropertyValue (text, ref index, endIndex, out quoted)) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - value = Encoding.UTF8.GetString (text, valueIndex, index - valueIndex); - - if (quoted) - value = MimeUtils.Unquote (value); - - var propspec = new AuthenticationMethodProperty (ptype, property, value, quoted); - resinfo.Properties.Add (propspec); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex || text[index] == (byte) ';') - break; - - tokenIndex = index; - - if (!SkipKeyword (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid propspec token at offset {0}", tokenIndex), tokenIndex, index); - - return false; - } - - value = Encoding.ASCII.GetString (text, tokenIndex, index - tokenIndex); - } while (true); - - // skip over ';' - index++; - } - - return true; - } - - static bool TryParse (byte[] text, ref int index, int endIndex, bool throwOnError, out AuthenticationResults authres) - { - int? instance = null; - string srvid = null; - string value; - bool quoted; - - authres = null; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - do { - int start = index; - - if (index >= endIndex || !SkipValue (text, ref index, endIndex, out quoted)) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete authserv-id token at offset {0}", start), start, index); - - return false; - } - - value = Encoding.UTF8.GetString (text, start, index - start); - - if (quoted) { - // this can only be the authserv-id token - srvid = MimeUtils.Unquote (value); - } else { - // this could either be the authserv-id or it could be "i=#" (ARC instance) - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index < endIndex && text[index] == (byte) '=') { - // probably i=# - if (instance.HasValue) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid token at offset {0}", start), start, index); - - return false; - } - - if (value != "i") { - // Office 365 Authentication-Results do not include an authserv-id token, so this is probably a method. - // Rewind the parser and start over again with the assumption that the Authentication-Results only - // contains methods. - // - // See https://github.com/jstedfast/MimeKit/issues/490 for details. - - authres = new AuthenticationResults (); - index = 0; - - return TryParseMethods (text, ref index, endIndex, throwOnError, authres); - } - - // skip over '=' - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - start = index; - - if (!ParseUtils.TryParseInt32 (text, ref index, endIndex, out int i)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid instance value at offset {0}", start), start, index); - - return false; - } - - instance = i; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Missing semi-colon after instance value at offset {0}", start), start, index); - - return false; - } - - if (text[index] != (byte) ';') { - if (throwOnError) - throw new ParseException (string.Format ("Unexpected token after instance value at offset {0}", index), index, index); - - return false; - } - - // skip over ';' - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - } else { - srvid = value; - } - } - } while (srvid == null); - - authres = new AuthenticationResults (srvid) { Instance = instance }; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) - return true; - - if (text[index] != (byte) ';') { - // might be the authres-version token - int start = index; - - if (!ParseUtils.TryParseInt32 (text, ref index, endIndex, out int version)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid authres-version at offset {0}", start), start, index); - - return false; - } - - authres.Version = version; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) - return true; - - if (text[index] != (byte) ';') { - if (throwOnError) - throw new ParseException (string.Format ("Unknown token at offset {0}", index), index, index); - - return false; - } - } - - // skip the ';' - index++; - - return TryParseMethods (text, ref index, endIndex, throwOnError, authres); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses an Authentication-Results header value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true if the authentication results were successfully parsed; otherwise, false. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed authentication results. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out AuthenticationResults authres) - { - ParseUtils.ValidateArguments (buffer, startIndex, length); - - int index = startIndex; - - return TryParse (buffer, ref index, startIndex + length, false, out authres); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses an Authentication-Results header value from the supplied buffer. - /// - /// true if the authentication results were successfully parsed; otherwise, false. - /// The input buffer. - /// The parsed authentication results. - /// - /// is null. - /// - public static bool TryParse (byte[] buffer, out AuthenticationResults authres) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - int index = 0; - - return TryParse (buffer, ref index, buffer.Length, false, out authres); - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses an Authentication-Results header value from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// The parsed . - /// The input buffer. - /// The start index of the buffer. - /// The length of the buffer. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// The could not be parsed. - /// - public static AuthenticationResults Parse (byte[] buffer, int startIndex, int length) - { - ParseUtils.ValidateArguments (buffer, startIndex, length); - - AuthenticationResults authres; - int index = startIndex; - - TryParse (buffer, ref index, startIndex + length, true, out authres); - - return authres; - } - - /// - /// Parse the specified input buffer into a new instance of the class. - /// - /// - /// Parses an Authentication-Results header value from the supplied buffer. - /// - /// The parsed . - /// The input buffer. - /// - /// is null. - /// - /// - /// The could not be parsed. - /// - public static AuthenticationResults Parse (byte[] buffer) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - AuthenticationResults authres; - int index = 0; - - TryParse (buffer, ref index, buffer.Length, true, out authres); - - return authres; - } - } - - /// - /// An authentication method results. - /// - /// - /// An authentication method results. - /// - public class AuthenticationMethodResult - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The method used for authentication. - /// - /// is null. - /// - internal AuthenticationMethodResult (string method) - { - if (method == null) - throw new ArgumentNullException (nameof (method)); - - Properties = new List (); - Method = method; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The method used for authentication. - /// The result of the authentication method. - /// - /// is null. - /// -or- - /// is null. - /// - public AuthenticationMethodResult (string method, string result) : this (method) - { - if (result == null) - throw new ArgumentNullException (nameof (result)); - - Result = result; - } - - /// - /// Get the Office365 method-specific authserv-id. - /// - /// - /// Gets the Office365 method-specific authserv-id. - /// An authentication service identifier is the authserv-id token - /// as defined in rfc7601. - /// Instead of specifying a single authentication service identifier at the - /// beginning of the header value, Office365 seems to provide a different - /// authentication service identifier for each method. - /// - /// The authserv-id token. - public string Office365AuthenticationServiceIdentifier { - get; internal set; - } - - /// - /// Get the authentication method. - /// - /// - /// Gets the authentication method. - /// - /// The authentication method. - public string Method { - get; private set; - } - - /// - /// Get the authentication method version. - /// - /// - /// Gets the authentication method version. - /// - /// The authentication method version. - public int? Version { - get; set; - } - - /// - /// Get the authentication method results. - /// - /// - /// Gets the authentication method results. - /// - /// The authentication method results. - public string Result { - get; internal set; - } - - /// - /// Get the comment regarding the authentication method result. - /// - /// - /// Gets the comment regarding the authentication method result. - /// - /// The comment regarding the authentication method result. - public string ResultComment { - get; set; - } - - /// - /// Get the action taken for the authentication method result. - /// - /// - /// Gets the action taken for the authentication method result. - /// - /// The action taken for the authentication method result. - public string Action { - get; internal set; - } - - /// - /// Get the reason for the authentication method result. - /// - /// - /// Gets the reason for the authentication method result. - /// - /// The reason for the authentication method result. - public string Reason { - get; set; - } - - /// - /// Get the properties used by the authentication method. - /// - /// - /// Gets the properties used by the authentication method. - /// - /// The properties used by the authentication method. - public List Properties { - get; private set; - } - - internal void Encode (FormatOptions options, StringBuilder builder, ref int lineLength) - { - // try to put the entire result on 1 line - var complete = ToString (); - - if (complete.Length + 1 < options.MaxLineLength) { - // if it fits, it sits... - if (lineLength + complete.Length + 1 > options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - } else { - builder.Append (' '); - lineLength++; - } - - lineLength += complete.Length; - builder.Append (complete); - return; - } - - // Note: if we've made it this far, then we can't put everything on one line... - - var tokens = new List (); - tokens.Add (" "); - - if (Office365AuthenticationServiceIdentifier != null) { - tokens.Add (Office365AuthenticationServiceIdentifier); - tokens.Add (";"); - tokens.Add (" "); - } - - if (Version.HasValue) { - var version = Version.Value.ToString (CultureInfo.InvariantCulture); - - if (Method.Length + 1 + version.Length + 1 + Result.Length < options.MaxLineLength) { - tokens.Add ($"{Method}/{version}={Result}"); - } else if (Method.Length + 1 + version.Length < options.MaxLineLength) { - tokens.Add ($"{Method}/{version}"); - tokens.Add ("="); - tokens.Add (Result); - } else { - tokens.Add (Method); - tokens.Add ("/"); - tokens.Add (version); - tokens.Add ("="); - tokens.Add (Result); - } - } else { - if (Method.Length + 1 + Result.Length < options.MaxLineLength) { - tokens.Add ($"{Method}={Result}"); - } else { - // we will have to break this up into individual tokens - tokens.Add (Method); - tokens.Add ("="); - tokens.Add (Result); - } - } - - if (!string.IsNullOrEmpty (ResultComment)) { - tokens.Add (" "); - tokens.Add ($"({ResultComment})"); - } - - if (!string.IsNullOrEmpty (Reason)) { - var reason = MimeUtils.Quote (Reason); - - tokens.Add (" "); - - if ("reason=".Length + reason.Length < options.MaxLineLength) { - tokens.Add ($"reason={reason}"); - } else { - tokens.Add ("reason="); - tokens.Add (reason); - } - } else if (!string.IsNullOrEmpty (Action)) { - var action = MimeUtils.Quote (Action); - - tokens.Add (" "); - - if ("action=".Length + action.Length < options.MaxLineLength) { - tokens.Add ($"action={action}"); - } else { - tokens.Add ("action="); - tokens.Add (action); - } - } - - for (int i = 0; i < Properties.Count; i++) - Properties[i].AppendTokens (options, tokens); - - builder.AppendTokens (options, ref lineLength, tokens); - } - - /// - /// Serializes the to a string. - /// - /// - /// Creates a string-representation of the . - /// - /// The serialized string. - public override string ToString () - { - var builder = new StringBuilder (); - - if (Office365AuthenticationServiceIdentifier != null) { - builder.Append (Office365AuthenticationServiceIdentifier); - builder.Append ("; "); - } - - builder.Append (Method); - - if (Version.HasValue) { - builder.Append ('/'); - builder.Append (Version.Value.ToString (CultureInfo.InvariantCulture)); - } - - builder.Append ('='); - builder.Append (Result); - - if (!string.IsNullOrEmpty (ResultComment)) { - builder.Append (" ("); - builder.Append (ResultComment); - builder.Append (')'); - } - - if (!string.IsNullOrEmpty (Reason)) { - builder.Append (" reason="); - builder.Append (MimeUtils.Quote (Reason)); - } else if (!string.IsNullOrEmpty (Action)) { - builder.Append (" action="); - builder.Append (MimeUtils.Quote (Action)); - } - - for (int i = 0; i < Properties.Count; i++) { - builder.Append (' '); - builder.Append (Properties[i]); - } - - return builder.ToString (); - } - } - - /// - /// An authentication method property. - /// - /// - /// An authentication method property. - /// - public class AuthenticationMethodProperty - { - static readonly char[] TokenSpecials = ByteExtensions.TokenSpecials.ToCharArray (); - bool? quoted; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The property type. - /// The name of the property. - /// The value of the property. - /// true if the property value was originally quoted; otherwise, false. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - internal AuthenticationMethodProperty (string ptype, string property, string value, bool? quoted) - { - if (ptype == null) - throw new ArgumentNullException (nameof (ptype)); - - if (property == null) - throw new ArgumentNullException (nameof (property)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - this.quoted = quoted; - PropertyType = ptype; - Property = property; - Value = value; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The property type. - /// The name of the property. - /// The value of the property. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public AuthenticationMethodProperty (string ptype, string property, string value) : this (ptype, property, value, null) - { - } - - /// - /// Get the type of the property. - /// - /// - /// Gets the type of the property. - /// - /// The type of the property. - public string PropertyType { - get; private set; - } - - /// - /// Get the property name. - /// - /// - /// Gets the property name. - /// - /// The name of the property. - public string Property { - get; private set; - } - - /// - /// Get the property value. - /// - /// - /// Gets the property value. - /// - /// The value of the property. - public string Value { - get; private set; - } - - internal void AppendTokens (FormatOptions options, List tokens) - { - var quote = quoted.HasValue ? quoted.Value : Value.IndexOfAny (TokenSpecials) != -1; - var value = quote ? MimeUtils.Quote (Value) : Value; - - tokens.Add (" "); - - if (PropertyType.Length + 1 + Property.Length + 1 + value.Length < options.MaxLineLength) { - tokens.Add ($"{PropertyType}.{Property}={value}"); - } else if (PropertyType.Length + 1 + Property.Length + 1 < options.MaxLineLength) { - tokens.Add ($"{PropertyType}.{Property}="); - tokens.Add (value); - } else { - tokens.Add (PropertyType); - tokens.Add ("."); - tokens.Add (Property); - tokens.Add ("="); - tokens.Add (value); - } - } - - /// - /// Serializes the to a string. - /// - /// - /// Creates a string-representation of the . - /// - /// The serialized string. - public override string ToString () - { - var quote = quoted.HasValue ? quoted.Value : Value.IndexOfAny (TokenSpecials) != -1; - var value = quote ? MimeUtils.Quote (Value) : Value; - - return $"{PropertyType}.{Property}={value}"; - } - } -} diff --git a/src/MimeKit/Cryptography/BouncyCastleCertificateExtensions.cs b/src/MimeKit/Cryptography/BouncyCastleCertificateExtensions.cs deleted file mode 100644 index 292b6d1..0000000 --- a/src/MimeKit/Cryptography/BouncyCastleCertificateExtensions.cs +++ /dev/null @@ -1,365 +0,0 @@ -// -// BouncyCastleCertificateExtensions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Collections.Generic; - -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Asn1.Smime; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Parameters; - -using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; - -namespace MimeKit.Cryptography { - /// - /// Extension methods for use with BouncyCastle X509Certificates. - /// - /// - /// Extension methods for use with BouncyCastle X509Certificates. - /// - public static class BouncyCastleCertificateExtensions - { - /// - /// Convert a BouncyCastle certificate into an X509Certificate2. - /// - /// - /// Converts a BouncyCastle certificate into an X509Certificate2. - /// - /// The X509Certificate2. - /// The BouncyCastle certificate. - /// - /// is null. - /// - public static X509Certificate2 AsX509Certificate2 (this X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - return new X509Certificate2 (certificate.GetEncoded ()); - } - - internal static bool IsSelfSigned (this X509Certificate certificate) - { - return certificate.SubjectDN.Equivalent (certificate.IssuerDN); - } - - /// - /// Gets the issuer name info. - /// - /// - /// For a list of available identifiers, see . - /// - /// The issuer name info. - /// The certificate. - /// The name identifier. - /// - /// is null. - /// - public static string GetIssuerNameInfo (this X509Certificate certificate, DerObjectIdentifier identifier) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - // FIXME: GetValueList() should be fixed to return IList - var list = certificate.IssuerDN.GetValueList (identifier); - if (list.Count == 0) - return string.Empty; - - return (string) list[0]; - } - - /// - /// Gets the issuer name info. - /// - /// - /// For a list of available identifiers, see . - /// - /// The issuer name info. - /// The certificate. - /// The name identifier. - /// - /// is null. - /// - public static string GetSubjectNameInfo (this X509Certificate certificate, DerObjectIdentifier identifier) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - // FIXME: GetValueList() should be fixed to return IList - var list = certificate.SubjectDN.GetValueList (identifier); - if (list.Count == 0) - return string.Empty; - - return (string) list[0]; - } - - /// - /// Gets the common name of the certificate. - /// - /// - /// Gets the common name of the certificate. - /// - /// The common name. - /// The certificate. - /// - /// is null. - /// - public static string GetCommonName (this X509Certificate certificate) - { - return certificate.GetSubjectNameInfo (X509Name.CN); - } - - /// - /// Gets the subject name of the certificate. - /// - /// - /// Gets the subject name of the certificate. - /// - /// The subject name. - /// The certificate. - /// - /// is null. - /// - public static string GetSubjectName (this X509Certificate certificate) - { - return certificate.GetSubjectNameInfo (X509Name.Name); - } - - /// - /// Gets the subject email address of the certificate. - /// - /// - /// The email address component of the certificate's Subject identifier is - /// sometimes used as a way of looking up certificates for a particular - /// user if a fingerprint is not available. - /// - /// The subject email address. - /// The certificate. - /// - /// is null. - /// - public static string GetSubjectEmailAddress (this X509Certificate certificate) - { - var address = certificate.GetSubjectNameInfo (X509Name.EmailAddress); - - if (!string.IsNullOrEmpty (address)) - return address; - - var alt = certificate.GetExtensionValue (X509Extensions.SubjectAlternativeName); - - if (alt == null) - return string.Empty; - - var seq = Asn1Sequence.GetInstance (Asn1Object.FromByteArray (alt.GetOctets ())); - - foreach (Asn1Encodable encodable in seq) { - var name = GeneralName.GetInstance (encodable); - - if (name.TagNo == GeneralName.Rfc822Name) - return ((IAsn1String) name.Name).GetString (); - } - - return null; - } - - internal static string AsHex (this byte[] blob) - { - var hex = new StringBuilder (blob.Length * 2); - - for (int i = 0; i < blob.Length; i++) - hex.Append (blob[i].ToString ("x2")); - - return hex.ToString (); - } - - /// - /// Gets the fingerprint of the certificate. - /// - /// - /// A fingerprint is a SHA-1 hash of the raw certificate data and is often used - /// as a unique identifier for a particular certificate in a certificate store. - /// - /// The fingerprint. - /// The certificate. - /// - /// is null. - /// - public static string GetFingerprint (this X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - var encoded = certificate.GetEncoded (); - var fingerprint = new byte[20]; - var sha1 = new Sha1Digest (); - - sha1.BlockUpdate (encoded, 0, encoded.Length); - sha1.DoFinal (fingerprint, 0); - - return fingerprint.AsHex (); - } - - /// - /// Gets the public key algorithm for the certificate. - /// - /// - /// Gets the public key algorithm for the ceretificate. - /// - /// The public key algorithm. - /// The certificate. - /// - /// is null. - /// - public static PublicKeyAlgorithm GetPublicKeyAlgorithm (this X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - var pubkey = certificate.GetPublicKey (); - - if (pubkey is DsaKeyParameters) - return PublicKeyAlgorithm.Dsa; - if (pubkey is RsaKeyParameters) - return PublicKeyAlgorithm.RsaGeneral; - if (pubkey is ElGamalKeyParameters) - return PublicKeyAlgorithm.ElGamalGeneral; - if (pubkey is ECKeyParameters) - return PublicKeyAlgorithm.EllipticCurve; - if (pubkey is DHKeyParameters) - return PublicKeyAlgorithm.DiffieHellman; - - return PublicKeyAlgorithm.None; - } - - internal static X509KeyUsageFlags GetKeyUsageFlags (bool[] usage) - { - var flags = X509KeyUsageFlags.None; - - if (usage == null || usage[(int) X509KeyUsageBits.DigitalSignature]) - flags |= X509KeyUsageFlags.DigitalSignature; - if (usage == null || usage[(int) X509KeyUsageBits.NonRepudiation]) - flags |= X509KeyUsageFlags.NonRepudiation; - if (usage == null || usage[(int) X509KeyUsageBits.KeyEncipherment]) - flags |= X509KeyUsageFlags.KeyEncipherment; - if (usage == null || usage[(int) X509KeyUsageBits.DataEncipherment]) - flags |= X509KeyUsageFlags.DataEncipherment; - if (usage == null || usage[(int) X509KeyUsageBits.KeyAgreement]) - flags |= X509KeyUsageFlags.KeyAgreement; - if (usage == null || usage[(int) X509KeyUsageBits.KeyCertSign]) - flags |= X509KeyUsageFlags.KeyCertSign; - if (usage == null || usage[(int) X509KeyUsageBits.CrlSign]) - flags |= X509KeyUsageFlags.CrlSign; - if (usage == null || usage[(int) X509KeyUsageBits.EncipherOnly]) - flags |= X509KeyUsageFlags.EncipherOnly; - if (usage == null || usage[(int) X509KeyUsageBits.DecipherOnly]) - flags |= X509KeyUsageFlags.DecipherOnly; - - return flags; - } - - /// - /// Gets the key usage flags. - /// - /// - /// The X.509 Key Usage Flags are used to determine which operations a certificate - /// may be used for. - /// - /// The key usage flags. - /// The certificate. - /// - /// is null. - /// - public static X509KeyUsageFlags GetKeyUsageFlags (this X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - return GetKeyUsageFlags (certificate.GetKeyUsage ()); - } - - static EncryptionAlgorithm[] DecodeEncryptionAlgorithms (byte[] rawData) - { - using (var memory = new MemoryStream (rawData, false)) { - using (var asn1 = new Asn1InputStream (memory)) { - var algorithms = new List (); - var sequence = asn1.ReadObject () as Asn1Sequence; - - if (sequence == null) - return null; - - for (int i = 0; i < sequence.Count; i++) { - var identifier = AlgorithmIdentifier.GetInstance (sequence[i]); - EncryptionAlgorithm algorithm; - - if (BouncyCastleSecureMimeContext.TryGetEncryptionAlgorithm (identifier, out algorithm)) - algorithms.Add (algorithm); - } - - return algorithms.ToArray (); - } - } - } - - /// - /// Get the encryption algorithms that can be used with an X.509 certificate. - /// - /// - /// Scans the X.509 certificate for the S/MIME capabilities extension. If found, - /// the supported encryption algorithms will be decoded and returned. - /// If no extension can be found, the - /// algorithm is returned. - /// - /// The encryption algorithms. - /// The X.509 certificate. - /// - /// is null. - /// - public static EncryptionAlgorithm[] GetEncryptionAlgorithms (this X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - var capabilities = certificate.GetExtensionValue (SmimeAttributes.SmimeCapabilities); - - if (capabilities != null) - return DecodeEncryptionAlgorithms (capabilities.GetOctets ()); - - return new EncryptionAlgorithm[] { EncryptionAlgorithm.TripleDes }; - } - - internal static bool IsDelta (this X509Crl crl) - { - var critical = crl.GetCriticalExtensionOids (); - - return critical != null ? critical.Contains (X509Extensions.DeltaCrlIndicator.Id) : false; - } - } -} diff --git a/src/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs b/src/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs deleted file mode 100644 index 616060f..0000000 --- a/src/MimeKit/Cryptography/BouncyCastleSecureMimeContext.cs +++ /dev/null @@ -1,1339 +0,0 @@ -// -// BouncyCastleSecureMimeContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -#if ENABLE_LDAP -using System.DirectoryServices.Protocols; -using SearchScope = System.DirectoryServices.Protocols.SearchScope; -#endif - -using Org.BouncyCastle.Cms; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Pkix; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Asn1.Cms; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Asn1.Smime; -using Org.BouncyCastle.X509.Store; -using Org.BouncyCastle.Utilities.Date; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Utilities.Collections; - -using AttributeTable = Org.BouncyCastle.Asn1.Cms.AttributeTable; -using IssuerAndSerialNumber = Org.BouncyCastle.Asn1.Cms.IssuerAndSerialNumber; - -using MimeKit.IO; - -namespace MimeKit.Cryptography -{ - /// - /// A Secure MIME (S/MIME) cryptography context. - /// - /// - /// An abstract S/MIME context built around the BouncyCastle API. - /// - public abstract class BouncyCastleSecureMimeContext : SecureMimeContext - { - static readonly string RsassaPssOid = PkcsObjectIdentifiers.IdRsassaPss.Id; - - HttpClient client; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new - /// - protected BouncyCastleSecureMimeContext () - { - client = new HttpClient (); - } - - /// - /// Get or set whether or not certificate revocation lists should be downloaded when verifying signatures. - /// - /// - /// Gets or sets whether or not certificate revocation lists should be downloaded when verifying - /// signatures. - /// If enabled, the will attempt to automatically download - /// Certificate Revocation Lists (CRLs) from the internet based on the CRL Distribution Point extension on - /// each certificate. - /// Enabling this feature opens the client up to potential privacy risks. An attacker - /// can generate a custom X.509 certificate containing a CRL Distribution Point or OCSP URL pointing to an - /// attacker-controlled server, thereby getting a notification when the user decrypts the message or verifies - /// its digital signature. - /// - /// true if CRLs should be downloaded automatically; otherwise, false. - public bool CheckCertificateRevocation { - get; set; - } - - /// - /// Get the X.509 certificate matching the specified selector. - /// - /// - /// Gets the first certificate that matches the specified selector. - /// This method is used when constructing a certificate chain if the S/MIME - /// signature does not include a signer's certificate. - /// - /// The certificate on success; otherwise null. - /// The search criteria for the certificate. - protected abstract X509Certificate GetCertificate (IX509Selector selector); - - /// - /// Get the private key for the certificate matching the specified selector. - /// - /// - /// Gets the private key for the first certificate that matches the specified selector. - /// This method is used when signing or decrypting content. - /// - /// The private key on success; otherwise, null. - /// The search criteria for the private key. - protected abstract AsymmetricKeyParameter GetPrivateKey (IX509Selector selector); - - /// - /// Get the trusted anchors. - /// - /// - /// A trusted anchor is a trusted root-level X.509 certificate, - /// generally issued by a certificate authority (CA). - /// This method is used to build a certificate chain while verifying - /// signed content. - /// - /// The trusted anchors. - protected abstract HashSet GetTrustedAnchors (); - - /// - /// Get the intermediate certificates. - /// - /// - /// An intermediate certificate is any certificate that exists between the root - /// certificate issued by a Certificate Authority (CA) and the certificate at - /// the end of the chain. - /// This method is used to build a certificate chain while verifying - /// signed content. - /// - /// The intermediate certificates. - protected abstract IX509Store GetIntermediateCertificates (); - - /// - /// Get the certificate revocation lists. - /// - /// - /// A Certificate Revocation List (CRL) is a list of certificate serial numbers issued - /// by a particular Certificate Authority (CA) that have been revoked, either by the CA - /// itself or by the owner of the revoked certificate. - /// - /// The certificate revocation lists. - protected abstract IX509Store GetCertificateRevocationLists (); - - /// - /// Get the date & time for the next scheduled certificate revocation list update for the specified issuer. - /// - /// - /// Gets the date & time for the next scheduled certificate revocation list update for the specified issuer. - /// - /// The date & time for the next update (in UTC). - /// The issuer. - protected abstract DateTime GetNextCertificateRevocationListUpdate (X509Name issuer); - - /// - /// Get the for the specified mailbox. - /// - /// - /// Constructs a with the appropriate certificate and - /// for the specified mailbox. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address. - /// - /// A . - /// The mailbox. - /// - /// A certificate for the specified could not be found. - /// - protected abstract CmsRecipient GetCmsRecipient (MailboxAddress mailbox); - - /// - /// Get a collection of CmsRecipients for the specified mailboxes. - /// - /// - /// Gets a collection of CmsRecipients for the specified mailboxes. - /// - /// A . - /// The mailboxes. - /// - /// is null. - /// - /// - /// A certificate for one or more of the specified could not be found. - /// - protected CmsRecipientCollection GetCmsRecipients (IEnumerable mailboxes) - { - if (mailboxes == null) - throw new ArgumentNullException (nameof (mailboxes)); - - var recipients = new CmsRecipientCollection (); - - foreach (var mailbox in mailboxes) - recipients.Add (GetCmsRecipient (mailbox)); - - return recipients; - } - - /// - /// Get the for the specified mailbox. - /// - /// - /// Constructs a with the appropriate signing certificate - /// for the specified mailbox. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address for database lookups. - /// - /// A . - /// The mailbox. - /// The preferred digest algorithm. - /// - /// A certificate for the specified could not be found. - /// - protected abstract CmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgorithm digestAlgo); - - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// This method is called when decoding digital signatures that include S/MIME capabilities in the metadata, allowing custom - /// implementations to update the X.509 certificate records with the list of preferred encryption algorithms specified by the - /// sending client. - /// - /// The certificate. - /// The encryption algorithm capabilities of the client (in preferred order). - /// The timestamp. - protected abstract void UpdateSecureMimeCapabilities (X509Certificate certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp); - - CmsAttributeTableGenerator AddSecureMimeCapabilities (AttributeTable signedAttributes) - { - var attr = GetSecureMimeCapabilitiesAttribute (true); - - // populate our signed attributes with some S/MIME capabilities - return new DefaultSignedAttributeTableGenerator (signedAttributes.Add (attr.AttrType, attr.AttrValues[0])); - } - - Stream Sign (CmsSigner signer, Stream content, bool encapsulate) - { - var unsignedAttributes = new SimpleAttributeTableGenerator (signer.UnsignedAttributes); - var signedAttributes = AddSecureMimeCapabilities (signer.SignedAttributes); - var signedData = new CmsSignedDataStreamGenerator (); - var digestOid = GetDigestOid (signer.DigestAlgorithm); - byte[] subjectKeyId = null; - - if (signer.SignerIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) { - var subjectKeyIdentifier = signer.Certificate.GetExtensionValue (X509Extensions.SubjectKeyIdentifier); - if (subjectKeyIdentifier != null) { - var id = (Asn1OctetString) Asn1Object.FromByteArray (subjectKeyIdentifier.GetOctets ()); - subjectKeyId = id.GetOctets (); - } - } - - if (signer.PrivateKey is RsaKeyParameters && signer.RsaSignaturePadding == RsaSignaturePadding.Pss) { - if (subjectKeyId == null) - signedData.AddSigner (signer.PrivateKey, signer.Certificate, RsassaPssOid, digestOid, signedAttributes, unsignedAttributes); - else - signedData.AddSigner (signer.PrivateKey, subjectKeyId, RsassaPssOid, digestOid, signedAttributes, unsignedAttributes); - } else if (subjectKeyId == null) { - signedData.AddSigner (signer.PrivateKey, signer.Certificate, digestOid, signedAttributes, unsignedAttributes); - } else { - signedData.AddSigner (signer.PrivateKey, subjectKeyId, digestOid, signedAttributes, unsignedAttributes); - } - - signedData.AddCertificates (signer.CertificateChain); - - var memory = new MemoryBlockStream (); - - using (var stream = signedData.Open (memory, encapsulate)) - content.CopyTo (stream, 4096); - - memory.Position = 0; - - return memory; - } - - /// - /// Cryptographically signs and encapsulates the content using the specified signer. - /// - /// - /// Cryptographically signs and encapsulates the content using the specified signer. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override ApplicationPkcs7Mime EncapsulatedSign (CmsSigner signer, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - return new ApplicationPkcs7Mime (SecureMimeType.SignedData, Sign (signer, content, true)); - } - - /// - /// Cryptographically signs and encapsulates the content using the specified signer and digest algorithm. - /// - /// - /// Cryptographically signs and encapsulates the content using the specified signer and digest algorithm. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - /// - /// A signing certificate could not be found for . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override ApplicationPkcs7Mime EncapsulatedSign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var cmsSigner = GetCmsSigner (signer, digestAlgo); - - return EncapsulatedSign (cmsSigner, content); - } - - /// - /// Cryptographically signs the content using the specified signer. - /// - /// - /// Cryptographically signs the content using the specified signer. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override ApplicationPkcs7Signature Sign (CmsSigner signer, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - return new ApplicationPkcs7Signature (Sign (signer, content, false)); - } - - /// - /// Cryptographically signs the content using the specified signer and digest algorithm. - /// - /// - /// Cryptographically signs the content using the specified signer and digest algorithm. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - /// - /// A signing certificate could not be found for . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override MimePart Sign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var cmsSigner = GetCmsSigner (signer, digestAlgo); - - return Sign (cmsSigner, content); - } - - X509Certificate GetCertificate (IX509Store store, SignerID signer) - { - var matches = store.GetMatches (signer); - - foreach (X509Certificate certificate in matches) - return certificate; - - return GetCertificate (signer); - } - - /// - /// Build a certificate chain. - /// - /// - /// Builds a certificate chain for the provided certificate to include when signing. - /// This method is ideal for use with custom - /// implementations when it is desirable to include the certificate chain - /// in the signature. - /// - /// The certificate to build the chain for. - /// The certificate chain, including the specified certificate. - protected IList BuildCertificateChain (X509Certificate certificate) - { - var selector = new X509CertStoreSelector (); - selector.Certificate = certificate; - - var intermediates = new X509CertificateStore (); - intermediates.Add (certificate); - - var parameters = new PkixBuilderParameters (GetTrustedAnchors (), selector); - parameters.ValidityModel = PkixParameters.PkixValidityModel; - parameters.AddStore (intermediates); - parameters.AddStore (GetIntermediateCertificates ()); - parameters.IsRevocationEnabled = false; - parameters.Date = new DateTimeObject (DateTime.UtcNow); - - var builder = new PkixCertPathBuilder (); - var result = builder.Build (parameters); - - var chain = new X509Certificate[result.CertPath.Certificates.Count]; - - for (int i = 0; i < chain.Length; i++) - chain[i] = (X509Certificate) result.CertPath.Certificates[i]; - - return chain; - } - - PkixCertPath BuildCertPath (HashSet anchors, IX509Store certificates, IX509Store crls, X509Certificate certificate, DateTime signingTime) - { - var selector = new X509CertStoreSelector (); - selector.Certificate = certificate; - - var intermediates = new X509CertificateStore (); - intermediates.Add (certificate); - - foreach (X509Certificate cert in certificates.GetMatches (null)) - intermediates.Add (cert); - - var parameters = new PkixBuilderParameters (anchors, selector); - parameters.AddStore (intermediates); - parameters.AddStore (crls); - - parameters.AddStore (GetIntermediateCertificates ()); - parameters.AddStore (GetCertificateRevocationLists ()); - - parameters.ValidityModel = PkixParameters.PkixValidityModel; - parameters.IsRevocationEnabled = false; - - if (signingTime != default (DateTime)) - parameters.Date = new DateTimeObject (signingTime); - - var builder = new PkixCertPathBuilder (); - var result = builder.Build (parameters); - - return result.CertPath; - } - - /// - /// Attempts to map a - /// to a . - /// - /// - /// Attempts to map a - /// to a . - /// - /// true if the algorithm identifier was successfully mapped; otherwise, false. - /// The algorithm identifier. - /// The encryption algorithm. - /// - /// is null. - /// - internal protected static bool TryGetDigestAlgorithm (AlgorithmIdentifier identifier, out DigestAlgorithm algorithm) - { - if (identifier == null) - throw new ArgumentNullException (nameof (identifier)); - - return TryGetDigestAlgorithm (identifier.Algorithm.Id, out algorithm); - } - - /// - /// Attempts to map a - /// to a . - /// - /// - /// Attempts to map a - /// to a . - /// - /// true if the algorithm identifier was successfully mapped; otherwise, false. - /// The algorithm identifier. - /// The encryption algorithm. - /// - /// is null. - /// - internal protected static bool TryGetEncryptionAlgorithm (AlgorithmIdentifier identifier, out EncryptionAlgorithm algorithm) - { - if (identifier == null) - throw new ArgumentNullException (nameof (identifier)); - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.Aes256Cbc) { - algorithm = EncryptionAlgorithm.Aes256; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.Aes192Cbc) { - algorithm = EncryptionAlgorithm.Aes192; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.Aes128Cbc) { - algorithm = EncryptionAlgorithm.Aes128; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.Camellia256Cbc) { - algorithm = EncryptionAlgorithm.Camellia256; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.Camellia192Cbc) { - algorithm = EncryptionAlgorithm.Camellia192; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.Camellia128Cbc) { - algorithm = EncryptionAlgorithm.Camellia128; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.Cast5Cbc) { - algorithm = EncryptionAlgorithm.Cast5; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.DesEde3Cbc) { - algorithm = EncryptionAlgorithm.TripleDes; - return true; - } - - if (identifier.Algorithm.Id == Blowfish.Id) { - algorithm = EncryptionAlgorithm.Blowfish; - return true; - } - - if (identifier.Algorithm.Id == Twofish.Id) { - algorithm = EncryptionAlgorithm.Twofish; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.SeedCbc) { - algorithm = EncryptionAlgorithm.Seed; - return true; - } - - if (identifier.Algorithm.Id == SmimeCapability.DesCbc.Id) { - algorithm = EncryptionAlgorithm.Des; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.IdeaCbc) { - algorithm = EncryptionAlgorithm.Idea; - return true; - } - - if (identifier.Algorithm.Id == CmsEnvelopedGenerator.RC2Cbc) { - if (identifier.Parameters is DerSequence) { - var param = (DerSequence) identifier.Parameters; - var version = (DerInteger) param[0]; - int bits = version.Value.IntValue; - - switch (bits) { - case 58: algorithm = EncryptionAlgorithm.RC2128; return true; - case 120: algorithm = EncryptionAlgorithm.RC264; return true; - case 160: algorithm = EncryptionAlgorithm.RC240; return true; - } - } else { - var param = (DerInteger) identifier.Parameters; - int bits = param.Value.IntValue; - - switch (bits) { - case 128: algorithm = EncryptionAlgorithm.RC2128; return true; - case 64: algorithm = EncryptionAlgorithm.RC264; return true; - case 40: algorithm = EncryptionAlgorithm.RC240; return true; - } - } - } - - algorithm = EncryptionAlgorithm.RC240; - - return false; - } - - async Task DownloadCrlsOverHttpAsync (string location, Stream stream, bool doAsync, CancellationToken cancellationToken) - { - try { - if (doAsync) { - using (var response = await client.GetAsync (location, cancellationToken).ConfigureAwait (false)) - await response.Content.CopyToAsync (stream).ConfigureAwait (false); - } else { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - cancellationToken.ThrowIfCancellationRequested (); - - var request = (HttpWebRequest) WebRequest.Create (location); - using (var response = request.GetResponse ()) { - var content = response.GetResponseStream (); - content.CopyTo (stream, 4096); - } -#else - using (var response = client.GetAsync (location, cancellationToken).GetAwaiter ().GetResult ()) - response.Content.CopyToAsync (stream).GetAwaiter ().GetResult (); -#endif - } - - return true; - } catch { - return false; - } - } - -#if ENABLE_LDAP - // https://msdn.microsoft.com/en-us/library/bb332056.aspx#sdspintro_topic3_lpadconn - bool DownloadCrlsOverLdap (string location, Stream stream, CancellationToken cancellationToken) - { - LdapUri uri; - - cancellationToken.ThrowIfCancellationRequested (); - - if (!LdapUri.TryParse (location, out uri) || string.IsNullOrEmpty (uri.Host) || string.IsNullOrEmpty (uri.DistinguishedName)) - return false; - - try { - // Note: Mono doesn't support this... - LdapDirectoryIdentifier identifier; - - if (uri.Port > 0) - identifier = new LdapDirectoryIdentifier (uri.Host, uri.Port, false, true); - else - identifier = new LdapDirectoryIdentifier (uri.Host, false, true); - - using (var ldap = new LdapConnection (identifier)) { - if (uri.Scheme.Equals ("ldaps", StringComparison.OrdinalIgnoreCase)) - ldap.SessionOptions.SecureSocketLayer = true; - - ldap.Bind (); - - var request = new SearchRequest (uri.DistinguishedName, uri.Filter, uri.Scope, uri.Attributes); - var response = (SearchResponse) ldap.SendRequest (request); - - foreach (SearchResultEntry entry in response.Entries) { - foreach (DirectoryAttribute attribute in entry.Attributes) { - var values = attribute.GetValues (typeof (byte[])); - - for (int i = 0; i < values.Length; i++) { - var buffer = (byte[]) values[i]; - - stream.Write (buffer, 0, buffer.Length); - } - } - } - } - - return true; - } catch { - return false; - } - } -#endif - - async Task DownloadCrlsAsync (X509Certificate certificate, bool doAsync, CancellationToken cancellationToken) - { - var nextUpdate = GetNextCertificateRevocationListUpdate (certificate.IssuerDN); - var now = DateTime.UtcNow; - Asn1OctetString cdp; - - if (nextUpdate > now) - return; - - if ((cdp = certificate.GetExtensionValue (X509Extensions.CrlDistributionPoints)) == null) - return; - - var asn1 = Asn1Object.FromByteArray (cdp.GetOctets ()); - var crlDistributionPoint = CrlDistPoint.GetInstance (asn1); - var points = crlDistributionPoint.GetDistributionPoints (); - - using (var stream = new MemoryBlockStream ()) { -#if ENABLE_LDAP - var ldapLocations = new List (); -#endif - bool downloaded = false; - - for (int i = 0; i < points.Length; i++) { - var generalNames = GeneralNames.GetInstance (points[i].DistributionPointName.Name).GetNames (); - for (int j = 0; j < generalNames.Length && !downloaded; j++) { - if (generalNames[j].TagNo != GeneralName.UniformResourceIdentifier) - continue; - - var location = DerIA5String.GetInstance (generalNames[j].Name).GetString (); - var colon = location.IndexOf (':'); - - if (colon == -1) - continue; - - var protocol = location.Substring (0, colon).ToLowerInvariant (); - - switch (protocol) { - case "https": case "http": - downloaded = await DownloadCrlsOverHttpAsync (location, stream, doAsync, cancellationToken).ConfigureAwait (false); - break; -#if ENABLE_LDAP - case "ldaps": case "ldap": - // Note: delay downloading from LDAP urls in case we find an HTTP url instead since LDAP - // won't be as reliable on Mono systems which do not implement the LDAP functionality. - ldapLocations.Add (location); - break; -#endif - } - } - } - -#if ENABLE_LDAP - for (int i = 0; i < ldapLocations.Count && !downloaded; i++) - downloaded = DownloadCrlsOverLdap (ldapLocations[i], stream, cancellationToken); -#endif - - if (!downloaded) - return; - - stream.Position = 0; - - var parser = new X509CrlParser (); - foreach (X509Crl crl in parser.ReadCrls (stream)) - Import (crl); - } - } - - /// - /// Get the list of digital signatures. - /// - /// - /// Gets the list of digital signatures. - /// This method is useful to call from within any custom - /// Verify - /// method that you may implement in your own class. - /// - /// The digital signatures. - /// The CMS signed data parser. - /// Whether or not the operation should be done asynchronously. - /// The cancellation token. - async Task GetDigitalSignaturesAsync (CmsSignedDataParser parser, bool doAsync, CancellationToken cancellationToken) - { - var certificates = parser.GetCertificates ("Collection"); - var signatures = new List (); - var crls = parser.GetCrls ("Collection"); - var store = parser.GetSignerInfos (); - - foreach (SignerInformation signerInfo in store.GetSigners ()) { - var certificate = GetCertificate (certificates, signerInfo.SignerID); - var signature = new SecureMimeDigitalSignature (signerInfo, certificate); - - if (CheckCertificateRevocation && certificate != null) - await DownloadCrlsAsync (certificate, doAsync, cancellationToken).ConfigureAwait (false); - - if (certificate != null) { - Import (certificate); - - if (signature.EncryptionAlgorithms.Length > 0 && signature.CreationDate != default (DateTime)) - UpdateSecureMimeCapabilities (certificate, signature.EncryptionAlgorithms, signature.CreationDate); - } - - var anchors = GetTrustedAnchors (); - - try { - signature.Chain = BuildCertPath (anchors, certificates, crls, certificate, signature.CreationDate); - } catch (Exception ex) { - signature.ChainException = ex; - } - - signatures.Add (signature); - } - - return new DigitalSignatureCollection (signatures); - } - - /// - /// Verify the specified content using the detached signature data. - /// - /// - /// Verifies the specified content using the detached signature data. - /// - /// A list of the digital signatures. - /// The content. - /// The detached signature data. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public override DigitalSignatureCollection Verify (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) - { - if (content == null) - throw new ArgumentNullException (nameof (content)); - - if (signatureData == null) - throw new ArgumentNullException (nameof (signatureData)); - - var parser = new CmsSignedDataParser (new CmsTypedStream (content), signatureData); - var signed = parser.GetSignedContent (); - - signed.Drain (); - - return GetDigitalSignaturesAsync (parser, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously verify the specified content using the detached signature data. - /// - /// - /// Verifies the specified content using the detached signature data. - /// - /// A list of the digital signatures. - /// The content. - /// The detached signature data. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public override Task VerifyAsync (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) - { - if (content == null) - throw new ArgumentNullException (nameof (content)); - - if (signatureData == null) - throw new ArgumentNullException (nameof (signatureData)); - - var parser = new CmsSignedDataParser (new CmsTypedStream (content), signatureData); - var signed = parser.GetSignedContent (); - - signed.Drain (); - - return GetDigitalSignaturesAsync (parser, true, cancellationToken); - } - - /// - /// Verify the digital signatures of the specified signed data and extract the original content. - /// - /// - /// Verifies the digital signatures of the specified signed data and extracts the original content. - /// - /// The list of digital signatures. - /// The signed data. - /// The extracted MIME entity. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The extracted content could not be parsed as a MIME entity. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public override DigitalSignatureCollection Verify (Stream signedData, out MimeEntity entity, CancellationToken cancellationToken = default (CancellationToken)) - { - if (signedData == null) - throw new ArgumentNullException (nameof (signedData)); - - var parser = new CmsSignedDataParser (signedData); - var signed = parser.GetSignedContent (); - - entity = MimeEntity.Load (signed.ContentStream, cancellationToken); - signed.Drain (); - - return GetDigitalSignaturesAsync (parser, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Verify the digital signatures of the specified signed data and extract the original content. - /// - /// - /// Verifies the digital signatures of the specified signed data and extracts the original content. - /// - /// The extracted content stream. - /// The signed data. - /// The digital signatures. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public override Stream Verify (Stream signedData, out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) - { - if (signedData == null) - throw new ArgumentNullException (nameof (signedData)); - - var parser = new CmsSignedDataParser (signedData); - var signed = parser.GetSignedContent (); - var content = new MemoryBlockStream (); - - signed.ContentStream.CopyTo (content, 4096); - content.Position = 0; - signed.Drain (); - - signatures = GetDigitalSignaturesAsync (parser, false, cancellationToken).GetAwaiter ().GetResult (); - - return content; - } - - class CmsRecipientInfoGenerator : RecipientInfoGenerator - { - readonly CmsRecipient recipient; - - public CmsRecipientInfoGenerator (CmsRecipient recipient) - { - this.recipient = recipient; - } - - IWrapper CreateWrapper (AlgorithmIdentifier keyExchangeAlgorithm) - { - string name; - - if (PkcsObjectIdentifiers.IdRsaesOaep.Id.Equals (keyExchangeAlgorithm.Algorithm.Id, StringComparison.Ordinal)) { - var oaepParameters = RsaesOaepParameters.GetInstance (keyExchangeAlgorithm.Parameters); - name = "RSA//OAEPWITH" + DigestUtilities.GetAlgorithmName (oaepParameters.HashAlgorithm.Algorithm) + "ANDMGF1Padding"; - } else if (PkcsObjectIdentifiers.RsaEncryption.Id.Equals (keyExchangeAlgorithm.Algorithm.Id, StringComparison.Ordinal)) { - name = "RSA//PKCS1Padding"; - } else { - name = keyExchangeAlgorithm.Algorithm.Id; - } - - return WrapperUtilities.GetWrapper (name); - } - - byte[] GenerateWrappedKey (KeyParameter contentEncryptionKey, AlgorithmIdentifier keyEncryptionAlgorithm, AsymmetricKeyParameter publicKey, SecureRandom random) - { - var keyWrapper = CreateWrapper (keyEncryptionAlgorithm); - var keyBytes = contentEncryptionKey.GetKey (); - - keyWrapper.Init (true, new ParametersWithRandom (publicKey, random)); - - return keyWrapper.Wrap (keyBytes, 0, keyBytes.Length); - } - - public RecipientInfo Generate (KeyParameter contentEncryptionKey, SecureRandom random) - { - var tbs = Asn1Object.FromByteArray (recipient.Certificate.GetTbsCertificate ()); - var certificate = TbsCertificateStructure.GetInstance (tbs); - var publicKey = recipient.Certificate.GetPublicKey (); - var publicKeyInfo = certificate.SubjectPublicKeyInfo; - AlgorithmIdentifier keyEncryptionAlgorithm; - - if (publicKey is RsaKeyParameters && recipient.RsaEncryptionPadding?.Scheme == RsaEncryptionPaddingScheme.Oaep) { - keyEncryptionAlgorithm = recipient.RsaEncryptionPadding.GetAlgorithmIdentifier (); - } else { - keyEncryptionAlgorithm = publicKeyInfo.AlgorithmID; - } - - var encryptedKeyBytes = GenerateWrappedKey (contentEncryptionKey, keyEncryptionAlgorithm, publicKey, random); - RecipientIdentifier recipientIdentifier = null; - - if (recipient.RecipientIdentifierType == SubjectIdentifierType.SubjectKeyIdentifier) { - var subjectKeyIdentifier = recipient.Certificate.GetExtensionValue (X509Extensions.SubjectKeyIdentifier); - recipientIdentifier = new RecipientIdentifier (subjectKeyIdentifier); - } - - if (recipientIdentifier == null) { - var issuerAndSerial = new IssuerAndSerialNumber (certificate.Issuer, certificate.SerialNumber.Value); - recipientIdentifier = new RecipientIdentifier (issuerAndSerial); - } - - return new RecipientInfo (new KeyTransRecipientInfo (recipientIdentifier, keyEncryptionAlgorithm, - new DerOctetString (encryptedKeyBytes))); - } - } - - Stream Envelope (CmsRecipientCollection recipients, Stream content) - { - var unique = new HashSet (); - var cms = new CmsEnvelopedDataGenerator (); - int count = 0; - - foreach (var recipient in recipients) { - if (unique.Add (recipient.Certificate)) { - cms.AddRecipientInfoGenerator (new CmsRecipientInfoGenerator (recipient)); - count++; - } - } - - if (count == 0) - throw new ArgumentException ("No recipients specified.", nameof (recipients)); - - var algorithm = GetPreferredEncryptionAlgorithm (recipients); - var input = new CmsProcessableInputStream (content); - CmsEnvelopedData envelopedData; - - switch (algorithm) { - case EncryptionAlgorithm.Aes128: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.Aes128Cbc); - break; - case EncryptionAlgorithm.Aes192: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.Aes192Cbc); - break; - case EncryptionAlgorithm.Aes256: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.Aes256Cbc); - break; - case EncryptionAlgorithm.Blowfish: - envelopedData = cms.Generate (input, Blowfish.Id); - break; - case EncryptionAlgorithm.Camellia128: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.Camellia128Cbc); - break; - case EncryptionAlgorithm.Camellia192: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.Camellia192Cbc); - break; - case EncryptionAlgorithm.Camellia256: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.Camellia256Cbc); - break; - case EncryptionAlgorithm.Cast5: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.Cast5Cbc); - break; - case EncryptionAlgorithm.Des: - envelopedData = cms.Generate (input, SmimeCapability.DesCbc.Id); - break; - case EncryptionAlgorithm.Idea: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.IdeaCbc); - break; - case EncryptionAlgorithm.RC240: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.RC2Cbc, 40); - break; - case EncryptionAlgorithm.RC264: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.RC2Cbc, 64); - break; - case EncryptionAlgorithm.RC2128: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.RC2Cbc, 128); - break; - case EncryptionAlgorithm.Seed: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.SeedCbc); - break; - case EncryptionAlgorithm.TripleDes: - envelopedData = cms.Generate (input, CmsEnvelopedGenerator.DesEde3Cbc); - break; - //case EncryptionAlgorithm.Twofish: - // envelopedData = cms.Generate (input, Twofish.Id); - // break; - default: - throw new NotSupportedException (string.Format ("The {0} encryption algorithm is not supported by the {1}.", algorithm, GetType ().Name)); - } - - return new MemoryStream (envelopedData.GetEncoded (), false); - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted content. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - return new ApplicationPkcs7Mime (SecureMimeType.EnvelopedData, Envelope (recipients, content)); - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// A certificate for one or more of the could not be found. - /// - /// - /// A certificate could not be found for one or more of the . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override MimePart Encrypt (IEnumerable recipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - return Encrypt (GetCmsRecipients (recipients), content); - } - - /// - /// Decrypt the specified encryptedData. - /// - /// - /// Decrypts the specified encryptedData. - /// - /// The decrypted . - /// The encrypted data. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public override MimeEntity Decrypt (Stream encryptedData, CancellationToken cancellationToken = default (CancellationToken)) - { - if (encryptedData == null) - throw new ArgumentNullException (nameof (encryptedData)); - - var parser = new CmsEnvelopedDataParser (encryptedData); - var recipients = parser.GetRecipientInfos (); - var algorithm = parser.EncryptionAlgorithmID; - AsymmetricKeyParameter key; - - foreach (RecipientInformation recipient in recipients.GetRecipients ()) { - if ((key = GetPrivateKey (recipient.RecipientID)) == null) - continue; - - var content = recipient.GetContent (key); - var memory = new MemoryStream (content, false); - - return MimeEntity.Load (memory, true, cancellationToken); - } - - throw new CmsException ("A suitable private key could not be found for decrypting."); - } - - /// - /// Decrypt the specified encryptedData to an output stream. - /// - /// - /// Decrypts the specified encryptedData to an output stream. - /// - /// The encrypted data. - /// The output stream. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override void DecryptTo (Stream encryptedData, Stream decryptedData) - { - if (encryptedData == null) - throw new ArgumentNullException (nameof (encryptedData)); - - if (decryptedData == null) - throw new ArgumentNullException (nameof (decryptedData)); - - var parser = new CmsEnvelopedDataParser (encryptedData); - var recipients = parser.GetRecipientInfos (); - var algorithm = parser.EncryptionAlgorithmID; - AsymmetricKeyParameter key; - - foreach (RecipientInformation recipient in recipients.GetRecipients ()) { - if ((key = GetPrivateKey (recipient.RecipientID)) == null) - continue; - - var content = recipient.GetContentStream (key); - content.ContentStream.CopyTo (decryptedData, 4096); - return; - } - - throw new CmsException ("A suitable private key could not be found for decrypting."); - } - - /// - /// Export the certificates for the specified mailboxes. - /// - /// - /// Exports the certificates for the specified mailboxes. - /// - /// A new instance containing - /// the exported keys. - /// The mailboxes. - /// - /// is null. - /// - /// - /// No mailboxes were specified. - /// - /// - /// A certificate for one or more of the could not be found. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override MimePart Export (IEnumerable mailboxes) - { - if (mailboxes == null) - throw new ArgumentNullException (nameof (mailboxes)); - - var certificates = new X509CertificateStore (); - int count = 0; - - foreach (var mailbox in mailboxes) { - var recipient = GetCmsRecipient (mailbox); - certificates.Add (recipient.Certificate); - count++; - } - - if (count == 0) - throw new ArgumentException ("No mailboxes specified.", nameof (mailboxes)); - - var cms = new CmsSignedDataStreamGenerator (); - cms.AddCertificates (certificates); - - var memory = new MemoryBlockStream (); - cms.Open (memory).Dispose (); - memory.Position = 0; - - return new ApplicationPkcs7Mime (SecureMimeType.CertsOnly, memory); - } - - /// - /// Releases all resources used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. - protected override void Dispose (bool disposing) - { - if (disposing && client != null) { - client.Dispose (); - client = null; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MimeKit/Cryptography/CertificateNotFoundException.cs b/src/MimeKit/Cryptography/CertificateNotFoundException.cs deleted file mode 100644 index 1d538e7..0000000 --- a/src/MimeKit/Cryptography/CertificateNotFoundException.cs +++ /dev/null @@ -1,115 +0,0 @@ -// -// CertificateNotFoundException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MimeKit.Cryptography { - /// - /// An exception that is thrown when a certificate could not be found for a specified mailbox. - /// - /// - /// An exception that is thrown when a certificate could not be found for a specified mailbox. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class CertificateNotFoundException : Exception - { -#if SERIALIZABLE - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The serialization info. - /// The stream context. - /// - /// is null. - /// - protected CertificateNotFoundException (SerializationInfo info, StreamingContext context) : base (info, context) - { - var text = info.GetString ("Mailbox"); - MailboxAddress mailbox; - - if (MailboxAddress.TryParse (text, out mailbox)) - Mailbox = mailbox; - } -#endif - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The mailbox that could not be resolved to a valid certificate. - /// A message explaining the error. - public CertificateNotFoundException (MailboxAddress mailbox, string message) : base (message) - { - Mailbox = mailbox; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Sets the - /// with information about the exception. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("Mailbox", Mailbox.ToString (true)); - } -#endif - - /// - /// Gets the mailbox address that could not be resolved to a certificate. - /// - /// - /// Gets the mailbox address that could not be resolved to a certificate. - /// - /// The mailbox address. - public MailboxAddress Mailbox { - get; private set; - } - } -} diff --git a/src/MimeKit/Cryptography/CmsRecipient.cs b/src/MimeKit/Cryptography/CmsRecipient.cs deleted file mode 100644 index aae14c1..0000000 --- a/src/MimeKit/Cryptography/CmsRecipient.cs +++ /dev/null @@ -1,256 +0,0 @@ -// -// CmsRecipient.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; - -using Org.BouncyCastle.X509; - -using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; - -namespace MimeKit.Cryptography { - /// - /// An S/MIME recipient. - /// - /// - /// If the X.509 certificates are known for each of the recipients, you - /// may wish to use a as opposed to having - /// the do its own certificate - /// lookups for each . - /// - public class CmsRecipient - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new based on the provided certificate. - /// If the X.509 certificate contains an S/MIME capability extension, the initial value of the - /// property will be set to whatever encryption algorithms are - /// defined by the S/MIME capability extension, otherwise int will be initialized to a list - /// containing only the Triple-Des encryption algorithm which should be safe to assume for all - /// modern S/MIME v3.x client implementations. - /// - /// The recipient's certificate. - /// The recipient identifier type. - /// - /// is null. - /// - public CmsRecipient (X509Certificate certificate, SubjectIdentifierType recipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - if (recipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; - else - RecipientIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - - EncryptionAlgorithms = certificate.GetEncryptionAlgorithms (); - Certificate = certificate; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new , loading the certificate from the specified stream. - /// If the X.509 certificate contains an S/MIME capability extension, the initial value of the - /// property will be set to whatever encryption algorithms are - /// defined by the S/MIME capability extension, otherwise int will be initialized to a list - /// containing only the Triple-Des encryption algorithm which should be safe to assume for all - /// modern S/MIME v3.x client implementations. - /// - /// The stream containing the recipient's certificate. - /// The recipient identifier type. - /// - /// is null. - /// - /// - /// The specified file does not contain a certificate. - /// - /// - /// An I/O error occurred. - /// - public CmsRecipient (Stream stream, SubjectIdentifierType recipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (recipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; - else - RecipientIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - - var parser = new X509CertificateParser (); - - Certificate = parser.ReadCertificate (stream); - - if (Certificate == null) - throw new FormatException (); - - EncryptionAlgorithms = Certificate.GetEncryptionAlgorithms (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new , loading the certificate from the specified file. - /// If the X.509 certificate contains an S/MIME capability extension, the initial value of the - /// property will be set to whatever encryption algorithms are - /// defined by the S/MIME capability extension, otherwise int will be initialized to a list - /// containing only the Triple-Des encryption algorithm which should be safe to assume for all - /// modern S/MIME v3.x client implementations. - /// - /// The file containing the recipient's certificate. - /// The recipient identifier type. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The specified file does not contain a certificate. - /// - /// - /// An I/O error occurred. - /// - public CmsRecipient (string fileName, SubjectIdentifierType recipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (recipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; - else - RecipientIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - - using (var stream = File.OpenRead (fileName)) { - var parser = new X509CertificateParser (); - - Certificate = parser.ReadCertificate (stream); - } - - if (Certificate == null) - throw new FormatException (); - - EncryptionAlgorithms = Certificate.GetEncryptionAlgorithms (); - } - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new based on the provided certificate. - /// If the X.509 certificate contains an S/MIME capability extension, the initial value of the - /// property will be set to whatever encryption algorithms are - /// defined by the S/MIME capability extension, otherwise int will be initialized to a list - /// containing only the Triple-Des encryption algorithm which should be safe to assume for all - /// modern S/MIME v3.x client implementations. - /// - /// The recipient's certificate. - /// The recipient identifier type. - /// - /// is null. - /// - public CmsRecipient (X509Certificate2 certificate, SubjectIdentifierType recipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - if (recipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - RecipientIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; - else - RecipientIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - - EncryptionAlgorithms = certificate.GetEncryptionAlgorithms (); - Certificate = certificate.AsBouncyCastleCertificate (); - } -#endif - - /// - /// Gets the recipient's certificate. - /// - /// - /// The certificate is used for the purpose of encrypting data. - /// - /// The certificate. - public X509Certificate Certificate { - get; private set; - } - - /// - /// Gets the recipient identifier type. - /// - /// - /// Specifies how the certificate should be looked up on the recipient's end. - /// - /// The recipient identifier type. - public SubjectIdentifierType RecipientIdentifierType { - get; private set; - } - - /// - /// Gets or sets the known S/MIME encryption capabilities of the - /// recipient's mail client, in their preferred order. - /// - /// - /// Provides the with an array of - /// encryption algorithms that are known to be supported by the - /// recpipient's client software and should be in the recipient's - /// order of preference. - /// - /// The encryption algorithms. - public EncryptionAlgorithm[] EncryptionAlgorithms { - get; set; - } - - /// - /// Get or set the RSA key encryption padding. - /// - /// - /// Gets or sets the padding to use for key encryption when - /// the 's public key is an RSA key. - /// - /// The encryption padding scheme. - public RsaEncryptionPadding RsaEncryptionPadding { - get; set; - } - } -} diff --git a/src/MimeKit/Cryptography/CmsRecipientCollection.cs b/src/MimeKit/Cryptography/CmsRecipientCollection.cs deleted file mode 100644 index e50234c..0000000 --- a/src/MimeKit/Cryptography/CmsRecipientCollection.cs +++ /dev/null @@ -1,208 +0,0 @@ -// -// CmsRecipientCollection.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace MimeKit.Cryptography { - /// - /// A collection of objects. - /// - /// - /// If the X.509 certificates are known for each of the recipients, you - /// may wish to use a as opposed to - /// using the methods that take a list of - /// objects. - /// - public class CmsRecipientCollection : ICollection - { - readonly IList recipients; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public CmsRecipientCollection () - { - recipients = new List (); - } - - #region ICollection implementation - - /// - /// Gets the number of recipients in the collection. - /// - /// - /// Indicates the number of recipients in the collection. - /// - /// The number of recipients in the collection. - public int Count { - get { return recipients.Count; } - } - - /// - /// Get a value indicating whether the is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Adds the specified recipient. - /// - /// - /// Adds the specified recipient. - /// - /// The recipient. - /// - /// is null. - /// - public void Add (CmsRecipient recipient) - { - if (recipient == null) - throw new ArgumentNullException (nameof (recipient)); - - recipients.Add (recipient); - } - - /// - /// Clears the recipient collection. - /// - /// - /// Removes all of the recipients from the collection. - /// - public void Clear () - { - recipients.Clear (); - } - - /// - /// Checks if the collection contains the specified recipient. - /// - /// - /// Determines whether or not the collection contains the specified recipient. - /// - /// true if the specified recipient exists; - /// otherwise false. - /// The recipient. - /// - /// is null. - /// - public bool Contains (CmsRecipient recipient) - { - if (recipient == null) - throw new ArgumentNullException (nameof (recipient)); - - return recipients.Contains (recipient); - } - - /// - /// Copies all of the recipients in the to the specified array. - /// - /// - /// Copies all of the recipients within the into the array, - /// starting at the specified array index. - /// - /// The array. - /// The array index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (CmsRecipient[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException (nameof (array)); - - if (arrayIndex < 0 || arrayIndex + Count > array.Length) - throw new ArgumentOutOfRangeException (nameof (arrayIndex)); - - recipients.CopyTo (array, arrayIndex); - } - - /// - /// Removes the specified recipient. - /// - /// - /// Removes the specified recipient. - /// - /// true if the recipient was removed; otherwise false. - /// The recipient. - /// - /// is null. - /// - public bool Remove (CmsRecipient recipient) - { - if (recipient == null) - throw new ArgumentNullException (nameof (recipient)); - - return recipients.Remove (recipient); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the collection of recipients. - /// - /// - /// Gets an enumerator for the collection of recipients. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return recipients.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the collection of recipients. - /// - /// - /// Gets an enumerator for the collection of recipients. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return recipients.GetEnumerator (); - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/CmsSigner.cs b/src/MimeKit/Cryptography/CmsSigner.cs deleted file mode 100644 index 47c8ee8..0000000 --- a/src/MimeKit/Cryptography/CmsSigner.cs +++ /dev/null @@ -1,464 +0,0 @@ -// -// CmsSigner.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections.Generic; - -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Asn1.Cms; - -using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; - -namespace MimeKit.Cryptography { - /// - /// An S/MIME signer. - /// - /// - /// If the X.509 certificate is known for the signer, you may wish to use a - /// as opposed to having the - /// do its own certificate lookup for the signer's . - /// - public class CmsSigner - { - /// - /// Initialize a new instance of the class. - /// - /// - /// The initial value of the will be set to - /// and both the - /// and properties - /// will be initialized to empty tables. - /// - CmsSigner () - { - UnsignedAttributes = new AttributeTable (new Dictionary ()); - SignedAttributes = new AttributeTable (new Dictionary ()); - DigestAlgorithm = DigestAlgorithm.Sha256; - } - - static bool CanSign (X509Certificate certificate) - { - var flags = certificate.GetKeyUsageFlags (); - - if (flags != X509KeyUsageFlags.None && (flags & SecureMimeContext.DigitalSignatureKeyUsageFlags) == 0) - return false; - - return true; - } - - static void CheckCertificateCanBeUsedForSigning (X509Certificate certificate) - { - if (!CanSign (certificate)) - throw new ArgumentException ("The certificate cannot be used for signing.", nameof (certificate)); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// The initial value of the will be set to - /// and both the - /// and properties - /// will be initialized to empty tables. - /// - /// The chain of certificates starting with the signer's certificate back to the root. - /// The signer's private key. - /// The scheme used for identifying the signer certificate. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// did not contain any certificates. - /// -or- - /// The certificate cannot be used for signing. - /// -or- - /// is not a private key. - /// - public CmsSigner (IEnumerable chain, AsymmetricKeyParameter key, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () - { - if (chain == null) - throw new ArgumentNullException (nameof (chain)); - - if (key == null) - throw new ArgumentNullException (nameof (key)); - - CertificateChain = new X509CertificateChain (chain); - - if (CertificateChain.Count == 0) - throw new ArgumentException ("The certificate chain was empty.", nameof (chain)); - - CheckCertificateCanBeUsedForSigning (CertificateChain[0]); - - if (!key.IsPrivate) - throw new ArgumentException ("The key must be a private key.", nameof (key)); - - if (signerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; - else - SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - - Certificate = CertificateChain[0]; - PrivateKey = key; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// The initial value of the will - /// be set to and both the - /// and properties will be - /// initialized to empty tables. - /// - /// The signer's certificate. - /// The signer's private key. - /// The scheme used for identifying the signer certificate. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// is not a private key. - /// - public CmsSigner (X509Certificate certificate, AsymmetricKeyParameter key, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - CheckCertificateCanBeUsedForSigning (certificate); - - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (!key.IsPrivate) - throw new ArgumentException ("The key must be a private key.", nameof (key)); - - if (signerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; - else - SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - - CertificateChain = new X509CertificateChain (); - CertificateChain.Add (certificate); - Certificate = certificate; - PrivateKey = key; - } - - void LoadPkcs12 (Stream stream, string password, SubjectIdentifierType signerIdentifierType) - { - var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ()); - bool hasPrivateKey = false; - - foreach (string alias in pkcs12.Aliases) { - if (!pkcs12.IsKeyEntry (alias)) - continue; - - var chain = pkcs12.GetCertificateChain (alias); - var key = pkcs12.GetKey (alias); - - if (!key.Key.IsPrivate) - continue; - - hasPrivateKey = true; - - if (chain.Length == 0 || !CanSign (chain[0].Certificate)) - continue; - - if (signerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; - else - SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - - CertificateChain = new X509CertificateChain (); - Certificate = chain[0].Certificate; - PrivateKey = key.Key; - - foreach (var entry in chain) - CertificateChain.Add (entry.Certificate); - - return; - } - - if (!hasPrivateKey) - throw new ArgumentException ("The stream did not contain a private key.", nameof (stream)); - - throw new ArgumentException ("The stream did not contain a certificate that could be used to create digital signatures.", nameof (stream)); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new , loading the X.509 certificate and private key - /// from the specified stream. - /// The initial value of the will - /// be set to and both the - /// and properties will be - /// initialized to empty tables. - /// - /// The raw certificate and key data in pkcs12 format. - /// The password to unlock the stream. - /// The scheme used for identifying the signer certificate. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain a private key. - /// -or- - /// does not contain a certificate that could be used for signing. - /// - /// - /// An I/O error occurred. - /// - public CmsSigner (Stream stream, string password, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - LoadPkcs12 (stream, password, signerIdentifierType); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new , loading the X.509 certificate and private key - /// from the specified file. - /// The initial value of the will - /// be set to and both the - /// and properties will be - /// initialized to empty tables. - /// - /// The raw certificate and key data in pkcs12 format. - /// The password to unlock the stream. - /// The scheme used for identifying the signer certificate. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// -or- - /// does not contain a private key. - /// -or- - /// does not contain a certificate that could be used for signing. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An I/O error occurred. - /// - public CmsSigner (string fileName, string password, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - using (var stream = File.OpenRead (fileName)) - LoadPkcs12 (stream, password, signerIdentifierType); - } - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - /// - /// Initialize a new instance of the class. - /// - /// - /// The initial value of the will - /// be set to and both the - /// and properties will be - /// initialized to empty tables. - /// - /// The signer's certificate. - /// The scheme used for identifying the signer certificate. - /// - /// is null. - /// - /// - /// cannot be used for signing. - /// - public CmsSigner (X509Certificate2 certificate, SubjectIdentifierType signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber) : this () - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - if (!certificate.HasPrivateKey) - throw new ArgumentException ("The certificate does not contain a private key.", nameof (certificate)); - - var cert = certificate.AsBouncyCastleCertificate (); - var key = certificate.PrivateKey.AsAsymmetricKeyParameter (); - - CheckCertificateCanBeUsedForSigning (cert); - - if (signerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - SignerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber; - else - SignerIdentifierType = SubjectIdentifierType.SubjectKeyIdentifier; - - CertificateChain = new X509CertificateChain (); - CertificateChain.Add (cert); - Certificate = cert; - PrivateKey = key; - } -#endif - - /// - /// Get the signer's certificate. - /// - /// - /// The signer's certificate that contains a public key that can be used for - /// verifying the digital signature. - /// - /// The signer's certificate. - public X509Certificate Certificate { - get; private set; - } - - /// - /// Get the certificate chain. - /// - /// - /// Gets the certificate chain. - /// - /// The certificate chain. - public X509CertificateChain CertificateChain { - get; private set; - } - - /// - /// Get or set the digest algorithm. - /// - /// - /// Specifies which digest algorithm to use to generate the - /// cryptographic hash of the content being signed. - /// - /// The digest algorithm. - public DigestAlgorithm DigestAlgorithm { - get; set; - } - - /// - /// Get the private key. - /// - /// - /// The private key used for signing. - /// - /// The private key. - public AsymmetricKeyParameter PrivateKey { - get; private set; - } - - /// - /// Get or set the RSA signature padding scheme. - /// - /// - /// Gets or sets the signature padding scheme to use for signing when - /// the is an RSA key. - /// - /// The signature padding scheme. - [Obsolete ("Use RsaSignaturePadding instead.")] - public RsaSignaturePaddingScheme RsaSignaturePaddingScheme { - get { return RsaSignaturePadding?.Scheme ?? RsaSignaturePaddingScheme.Pkcs1; } - set { - switch (value) { - case RsaSignaturePaddingScheme.Pkcs1: RsaSignaturePadding = RsaSignaturePadding.Pkcs1; break; - case RsaSignaturePaddingScheme.Pss: RsaSignaturePadding = RsaSignaturePadding.Pss; break; - default: throw new ArgumentOutOfRangeException (nameof (value)); - } - } - } - - /// - /// Get or set the RSA signature padding. - /// - /// - /// Gets or sets the signature padding to use for signing when - /// the is an RSA key. - /// - /// The signature padding scheme. - public RsaSignaturePadding RsaSignaturePadding { - get; set; - } - - /// - /// Gets the signer identifier type. - /// - /// - /// Specifies how the certificate should be looked up on the recipient's end. - /// - /// The signer identifier type. - public SubjectIdentifierType SignerIdentifierType { - get; private set; - } - - /// - /// Get or set the signed attributes. - /// - /// - /// A table of attributes that should be included in the signature. - /// - /// The signed attributes. - public AttributeTable SignedAttributes { - get; set; - } - - /// - /// Get or set the unsigned attributes. - /// - /// - /// A table of attributes that should not be signed in the signature, - /// but still included in transport. - /// - /// The unsigned attributes. - public AttributeTable UnsignedAttributes { - get; set; - } - } -} diff --git a/src/MimeKit/Cryptography/CryptographyContext.cs b/src/MimeKit/Cryptography/CryptographyContext.cs deleted file mode 100644 index 60bed14..0000000 --- a/src/MimeKit/Cryptography/CryptographyContext.cs +++ /dev/null @@ -1,648 +0,0 @@ -// -// CryptographyContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Reflection; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MimeKit.Cryptography { - /// - /// An abstract cryptography context. - /// - /// - /// Generally speaking, applications should not use a - /// directly, but rather via higher level APIs such as , - /// and . - /// - public abstract class CryptographyContext : IDisposable - { - const string SubclassAndRegisterFormat = "You need to subclass {0} and then register it with MimeKit.Cryptography.CryptographyContext.Register()."; - static Func SecureMimeContextFactory; - static Func PgpContextFactory; - static readonly object mutex = new object (); - - EncryptionAlgorithm[] encryptionAlgorithmRank; - DigestAlgorithm[] digestAlgorithmRank; - - int enabledEncryptionAlgorithms; - int enabledDigestAlgorithms; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// By default, only the 3DES encryption algorithm and the SHA-1 digest algorithm are enabled. - /// - protected CryptographyContext () - { - encryptionAlgorithmRank = new[] { - EncryptionAlgorithm.TripleDes - }; - - Enable (EncryptionAlgorithm.TripleDes); - - digestAlgorithmRank = new[] { - DigestAlgorithm.Sha1 - }; - - Enable (DigestAlgorithm.Sha1); - } - - /// - /// Get the signature protocol. - /// - /// - /// The signature protocol is used by - /// in order to determine what the protocol parameter of the Content-Type - /// header should be. - /// - /// The signature protocol. - public abstract string SignatureProtocol { get; } - - /// - /// Get the encryption protocol. - /// - /// - /// The encryption protocol is used by - /// in order to determine what the protocol parameter of the Content-Type - /// header should be. - /// - /// The encryption protocol. - public abstract string EncryptionProtocol { get; } - - /// - /// Get the key exchange protocol. - /// - /// - /// The key exchange protocol is really only used for OpenPGP. - /// - /// The key exchange protocol. - public abstract string KeyExchangeProtocol { get; } - -#if NOT_YET - /// - /// Gets or sets a value indicating whether this allows online - /// certificate retrieval. - /// - /// true if online certificate retrieval should be allowed; otherwise, false. - public bool AllowOnlineCertificateRetrieval { get; set; } - - /// - /// Gets or sets the online certificate retrieval timeout. - /// - /// The online certificate retrieval timeout. - public TimeSpan OnlineCertificateRetrievalTimeout { get; set; } -#endif - - /// - /// Get the preferred rank order for the encryption algorithms; from the most preferred to the least. - /// - /// - /// Gets the preferred rank order for the encryption algorithms; from the most preferred to the least. - /// - /// The preferred encryption algorithm ranking. - protected EncryptionAlgorithm[] EncryptionAlgorithmRank { - get { return encryptionAlgorithmRank; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (value.Length == 0) - throw new ArgumentException ("The array of encryption algorithms cannot be empty.", nameof (value)); - - encryptionAlgorithmRank = value; - } - } - - /// - /// Get the enabled encryption algorithms in ranked order. - /// - /// - /// Gets the enabled encryption algorithms in ranked order. - /// - /// The enabled encryption algorithms. - public EncryptionAlgorithm[] EnabledEncryptionAlgorithms { - get { - var algorithms = new List (); - - foreach (var algorithm in EncryptionAlgorithmRank) { - if (IsEnabled (algorithm)) - algorithms.Add (algorithm); - } - - return algorithms.ToArray (); - } - } - - /// - /// Enable the encryption algorithm. - /// - /// - /// Enables the encryption algorithm. - /// - /// The encryption algorithm. - public void Enable (EncryptionAlgorithm algorithm) - { - enabledEncryptionAlgorithms |= 1 << (int) algorithm; - } - - /// - /// Disable the encryption algorithm. - /// - /// - /// Disables the encryption algorithm. - /// - /// The encryption algorithm. - public void Disable (EncryptionAlgorithm algorithm) - { - enabledEncryptionAlgorithms &= ~(1 << (int) algorithm); - } - - /// - /// Check whether the specified encryption algorithm is enabled. - /// - /// - /// Determines whether the specified encryption algorithm is enabled. - /// - /// true if the specified encryption algorithm is enabled; otherwise, false. - /// The encryption algorithm. - public bool IsEnabled (EncryptionAlgorithm algorithm) - { - return (enabledEncryptionAlgorithms & (1 << (int) algorithm)) != 0; - } - - /// - /// Get the preferred rank order for the digest algorithms; from the most preferred to the least. - /// - /// - /// Gets the preferred rank order for the digest algorithms; from the most preferred to the least. - /// - /// The preferred encryption algorithm ranking. - protected DigestAlgorithm[] DigestAlgorithmRank { - get { return digestAlgorithmRank; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (value.Length == 0) - throw new ArgumentException ("The array of digest algorithms cannot be empty.", nameof (value)); - - digestAlgorithmRank = value; - } - } - - /// - /// Get the enabled digest algorithms in ranked order. - /// - /// - /// Gets the enabled digest algorithms in ranked order. - /// - /// The enabled encryption algorithms. - public DigestAlgorithm[] EnabledDigestAlgorithms { - get { - var algorithms = new List (); - - foreach (var algorithm in DigestAlgorithmRank) { - if (IsEnabled (algorithm)) - algorithms.Add (algorithm); - } - - return algorithms.ToArray (); - } - } - - /// - /// Enable the digest algorithm. - /// - /// - /// Enables the digest algorithm. - /// - /// The digest algorithm. - public void Enable (DigestAlgorithm algorithm) - { - enabledDigestAlgorithms |= 1 << (int) algorithm; - } - - /// - /// Disable the digest algorithm. - /// - /// - /// Disables the digest algorithm. - /// - /// The digest algorithm. - public void Disable (DigestAlgorithm algorithm) - { - enabledDigestAlgorithms &= ~(1 << (int) algorithm); - } - - /// - /// Check whether the specified digest algorithm is enabled. - /// - /// - /// Determines whether the specified digest algorithm is enabled. - /// - /// true if the specified digest algorithm is enabled; otherwise, false. - /// The digest algorithm. - public bool IsEnabled (DigestAlgorithm algorithm) - { - return (enabledDigestAlgorithms & (1 << (int) algorithm)) != 0; - } - - /// - /// Check whether or not the specified protocol is supported by the . - /// - /// - /// Used in order to make sure that the protocol parameter value specified in either a multipart/signed - /// or multipart/encrypted part is supported by the supplied cryptography context. - /// - /// true if the protocol is supported; otherwise false - /// The protocol. - /// - /// is null. - /// - public abstract bool Supports (string protocol); - - /// - /// Get the string name of the digest algorithm for use with the micalg parameter of a multipart/signed part. - /// - /// - /// Maps the to the appropriate string identifier - /// as used by the micalg parameter value of a multipart/signed Content-Type - /// header. - /// - /// The micalg value. - /// The digest algorithm. - /// - /// is out of range. - /// - public abstract string GetDigestAlgorithmName (DigestAlgorithm micalg); - - /// - /// Get the digest algorithm from the micalg parameter value in a multipart/signed part. - /// - /// - /// Maps the micalg parameter value string back to the appropriate . - /// - /// The digest algorithm. - /// The micalg parameter value. - /// - /// is null. - /// - public abstract DigestAlgorithm GetDigestAlgorithm (string micalg); - - /// - /// Check whether or not a particular mailbox address can be used for signing. - /// - /// - /// Checks whether or not as particular mailbocx address can be used for signing. - /// - /// true if the mailbox address can be used for signing; otherwise, false. - /// The signer. - /// - /// is null. - /// - public abstract bool CanSign (MailboxAddress signer); - - /// - /// Check whether or not the cryptography context can encrypt to a particular recipient. - /// - /// - /// Checks whether or not the cryptography context can be used to encrypt to a particular recipient. - /// - /// true if the cryptography context can be used to encrypt to the designated recipient; otherwise, false. - /// The recipient's mailbox address. - /// - /// is null. - /// - public abstract bool CanEncrypt (MailboxAddress mailbox); - - /// - /// Cryptographically sign the content. - /// - /// - /// Cryptographically signs the content using the specified signer and digest algorithm. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - /// - /// A signing certificate could not be found for . - /// - public abstract MimePart Sign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content); - - /// - /// Verify the specified content using the detached signatureData. - /// - /// - /// Verifies the specified content using the detached signatureData. - /// - /// A list of digital signatures. - /// The content. - /// The signature data. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public abstract DigitalSignatureCollection Verify (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously verify the specified content using the detached signatureData. - /// - /// - /// Verifies the specified content using the detached signatureData. - /// - /// A list of digital signatures. - /// The content. - /// The signature data. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public abstract Task VerifyAsync (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Encrypt the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance containing the encrypted data. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// A certificate could not be found for one or more of the . - /// - public abstract MimePart Encrypt (IEnumerable recipients, Stream content); - - /// - /// Decrypt the specified encryptedData. - /// - /// - /// Decrypts the specified encryptedData. - /// - /// The decrypted . - /// The encrypted data. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public abstract MimeEntity Decrypt (Stream encryptedData, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Imports the public certificates or keys from the specified stream. - /// - /// - /// Imports the public certificates or keys from the specified stream. - /// - /// The raw certificate or key data. - /// - /// is null. - /// - /// - /// Importing keys is not supported by this cryptography context. - /// - public abstract void Import (Stream stream); - - /// - /// Exports the keys for the specified mailboxes. - /// - /// - /// Exports the keys for the specified mailboxes. - /// - /// A new instance containing the exported keys. - /// The mailboxes. - /// - /// is null. - /// - /// - /// was empty. - /// - /// - /// Exporting keys is not supported by this cryptography context. - /// - public abstract MimePart Export (IEnumerable mailboxes); - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected virtual void Dispose (bool disposing) - { - } - - /// - /// Releases all resources used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - } - - /// - /// Creates a new for the specified protocol. - /// - /// - /// Creates a new for the specified protocol. - /// The default types can over overridden by calling - /// the method with the preferred type. - /// - /// The for the protocol. - /// The protocol. - /// - /// is null. - /// - /// - /// There are no supported s that support - /// the specified . - /// - public static CryptographyContext Create (string protocol) - { - if (protocol == null) - throw new ArgumentNullException (nameof (protocol)); - - protocol = protocol.ToLowerInvariant (); - - lock (mutex) { - switch (protocol) { - case "application/x-pkcs7-signature": - case "application/pkcs7-signature": - case "application/x-pkcs7-mime": - case "application/pkcs7-mime": - case "application/x-pkcs7-keys": - case "application/pkcs7-keys": - if (SecureMimeContextFactory != null) - return SecureMimeContextFactory (); - - return new DefaultSecureMimeContext (); - case "application/x-pgp-signature": - case "application/pgp-signature": - case "application/x-pgp-encrypted": - case "application/pgp-encrypted": - case "application/x-pgp-keys": - case "application/pgp-keys": - if (PgpContextFactory != null) - return PgpContextFactory (); - - throw new NotSupportedException (string.Format (SubclassAndRegisterFormat, "MimeKit.Cryptography.OpenPgpContext or MimeKit.Cryptography.GnuPGContext")); - default: - throw new NotSupportedException (); - } - } - } - - /// - /// Registers a default or . - /// - /// - /// Registers the specified type as the default or - /// . - /// - /// A custom subclass of or - /// . - /// - /// is null. - /// - /// - /// is not a subclass of - /// or . - /// -or- - /// does not have a parameterless constructor. - /// - public static void Register (Type type) - { - if (type == null) - throw new ArgumentNullException (nameof (type)); - -#if NETSTANDARD1_3 || NETSTANDARD1_6 - var info = type.GetTypeInfo (); -#else - var info = type; -#endif - var ctor = type.GetConstructor (new Type[0]); - - if (ctor == null) - throw new ArgumentException ("The specified type must have a parameterless constructor.", nameof (type)); - - if (info.IsSubclassOf (typeof (SecureMimeContext))) { - lock (mutex) { - SecureMimeContextFactory = () => (SecureMimeContext) ctor.Invoke (new object[0]); - } - } else if (info.IsSubclassOf (typeof (OpenPgpContextBase))) { - lock (mutex) { - PgpContextFactory = () => (OpenPgpContextBase) ctor.Invoke (new object[0]); - } - } else { - throw new ArgumentException ("The specified type must be a subclass of SecureMimeContext or OpenPgpContext.", nameof (type)); - } - } - - /// - /// Registers a default factory. - /// - /// - /// Registers a factory that will return a new instance of the default . - /// - /// A factory that creates a new instance of . - /// - /// is null. - /// - public static void Register (Func factory) - { - if (factory == null) - throw new ArgumentNullException (nameof (factory)); - - lock (mutex) { - SecureMimeContextFactory = factory; - } - } - - /// - /// Registers a default factory. - /// - /// - /// Registers a factory that will return a new instance of the default . - /// - /// A factory that creates a new instance of . - /// - /// is null. - /// - public static void Register (Func factory) - { - if (factory == null) - throw new ArgumentNullException(nameof (factory)); - - lock (mutex) { - PgpContextFactory = factory; - } - } - } -} diff --git a/src/MimeKit/Cryptography/DbExtensions.cs b/src/MimeKit/Cryptography/DbExtensions.cs deleted file mode 100644 index 0d66fa1..0000000 --- a/src/MimeKit/Cryptography/DbExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// DbExtensions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Data.Common; - -namespace MimeKit.Cryptography { - /// - /// Useful extensions for working with System.Data types. - /// - static class DbExtensions - { - /// - /// Creates a with the specified name and value and then adds it to the command's parameters. - /// - /// The database command. - /// The parameter name. - /// The parameter value. - public static int AddParameterWithValue (this DbCommand command, string name, object value) - { - var parameter = command.CreateParameter (); - parameter.ParameterName = name; - parameter.Value = value; - - return command.Parameters.Add (parameter); - } - } -} diff --git a/src/MimeKit/Cryptography/DefaultSecureMimeContext.cs b/src/MimeKit/Cryptography/DefaultSecureMimeContext.cs deleted file mode 100644 index ceee70c..0000000 --- a/src/MimeKit/Cryptography/DefaultSecureMimeContext.cs +++ /dev/null @@ -1,668 +0,0 @@ -// -// DefaultSecureMimeContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.Pkix; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.X509.Store; - -namespace MimeKit.Cryptography { - /// - /// A default implementation that uses - /// an SQLite database as a certificate and private key store. - /// - /// - /// The default S/MIME context is designed to be usable on any platform - /// where there exists a .NET runtime by storing certificates, CRLs, and - /// (encrypted) private keys in a SQL database. - /// - public class DefaultSecureMimeContext : BouncyCastleSecureMimeContext - { - const X509CertificateRecordFields CmsRecipientFields = X509CertificateRecordFields.Algorithms | X509CertificateRecordFields.Certificate; - const X509CertificateRecordFields CmsSignerFields = X509CertificateRecordFields.Certificate | X509CertificateRecordFields.PrivateKey; - const X509CertificateRecordFields AlgorithmFields = X509CertificateRecordFields.Id | X509CertificateRecordFields.Algorithms | X509CertificateRecordFields.AlgorithmsUpdated; - const X509CertificateRecordFields ImportPkcs12Fields = AlgorithmFields | X509CertificateRecordFields.Trusted | X509CertificateRecordFields.PrivateKey; - - /// - /// The default database path for certificates, private keys and CRLs. - /// - /// - /// On Microsoft Windows-based systems, this path will be something like C:\Users\UserName\AppData\Roaming\mimekit\smime.db. - /// On Unix systems such as Linux and Mac OS X, this path will be ~/.mimekit/smime.db. - /// - public static readonly string DefaultDatabasePath; - - readonly IX509CertificateDatabase dbase; - - static DefaultSecureMimeContext () - { - string path; - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (Path.DirectorySeparatorChar == '\\') { - var appData = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); - path = Path.Combine (appData, "Roaming\\mimekit"); - } else { - var home = Environment.GetFolderPath (Environment.SpecialFolder.Personal); - path = Path.Combine (home, ".mimekit"); - } -#else - path = ".mimekit"; -#endif - - DefaultDatabasePath = Path.Combine (path, "smime.db"); - } - - static void CheckIsAvailable () - { - if (!SqliteCertificateDatabase.IsAvailable) { - const string format = "SQLite is not available. Install the {0} nuget."; -#if NETSTANDARD1_3 || NETSTANDARD1_6 - throw new NotSupportedException (string.Format (format, "Microsoft.Data.Sqlite")); -#else - throw new NotSupportedException (string.Format (format, "System.Data.SQLite")); -#endif - } - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Allows the program to specify its own location for the SQLite database. If the file does not exist, - /// it will be created and the necessary tables and indexes will be constructed. - /// Requires linking with Mono.Data.Sqlite. - /// - /// The path to the SQLite database. - /// The password used for encrypting and decrypting the private keys. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The specified file path is empty. - /// - /// - /// Mono.Data.Sqlite is not available. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An error occurred reading the file. - /// - public DefaultSecureMimeContext (string fileName, string password) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - CheckIsAvailable (); - - var dir = Path.GetDirectoryName (fileName); - var exists = File.Exists (fileName); - - if (!string.IsNullOrEmpty (dir) && !Directory.Exists (dir)) - Directory.CreateDirectory (dir); - - dbase = new SqliteCertificateDatabase (fileName, password); - - if (!exists) { - // TODO: initialize our dbase with some root CA certificates. - } - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Allows the program to specify its own password for the default database. - /// Requires linking with Mono.Data.Sqlite. - /// - /// The password used for encrypting and decrypting the private keys. - /// - /// Mono.Data.Sqlite is not available. - /// - /// - /// The user does not have access to read the database at the default location. - /// - /// - /// An error occurred reading the database at the default location. - /// - public DefaultSecureMimeContext (string password) : this (DefaultDatabasePath, password) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Not recommended for production use as the password to unlock the private keys is hard-coded. - /// Requires linking with Mono.Data.Sqlite. - /// - /// - /// Mono.Data.Sqlite is not available. - /// - /// - /// The user does not have access to read the database at the default location. - /// - /// - /// An error occurred reading the database at the default location. - /// - public DefaultSecureMimeContext () : this (DefaultDatabasePath, "no.secret") - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is useful for supplying a custom . - /// - /// The certificate database. - /// - /// is null. - /// - public DefaultSecureMimeContext (IX509CertificateDatabase database) - { - if (database == null) - throw new ArgumentNullException (nameof (database)); - - dbase = database; - } - - /// - /// Check whether or not a particular mailbox address can be used for signing. - /// - /// - /// Checks whether or not as particular mailbocx address can be used for signing. - /// - /// true if the mailbox address can be used for signing; otherwise, false. - /// The signer. - /// - /// is null. - /// - public override bool CanSign (MailboxAddress signer) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - foreach (var record in dbase.Find (signer, DateTime.UtcNow, true, CmsSignerFields)) { - if (record.KeyUsage != X509KeyUsageFlags.None && (record.KeyUsage & SecureMimeContext.DigitalSignatureKeyUsageFlags) == 0) - continue; - - return true; - } - - return false; - } - - /// - /// Check whether or not the cryptography context can encrypt to a particular recipient. - /// - /// - /// Checks whether or not the cryptography context can be used to encrypt to a particular recipient. - /// - /// true if the cryptography context can be used to encrypt to the designated recipient; otherwise, false. - /// The recipient's mailbox address. - /// - /// is null. - /// - public override bool CanEncrypt (MailboxAddress mailbox) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - foreach (var record in dbase.Find (mailbox, DateTime.UtcNow, false, CmsRecipientFields)) { - if (record.KeyUsage != 0 && (record.KeyUsage & X509KeyUsageFlags.KeyEncipherment) == 0) - continue; - - return true; - } - - return false; - } - -#region implemented abstract members of SecureMimeContext - - /// - /// Gets the X.509 certificate matching the specified selector. - /// - /// - /// Gets the first certificate that matches the specified selector. - /// - /// The certificate on success; otherwise null. - /// The search criteria for the certificate. - protected override X509Certificate GetCertificate (IX509Selector selector) - { - return dbase.FindCertificates (selector).FirstOrDefault (); - } - - /// - /// Gets the private key for the certificate matching the specified selector. - /// - /// - /// Gets the private key for the first certificate that matches the specified selector. - /// - /// The private key on success; otherwise null. - /// The search criteria for the private key. - protected override AsymmetricKeyParameter GetPrivateKey (IX509Selector selector) - { - return dbase.FindPrivateKeys (selector).FirstOrDefault (); - } - - /// - /// Gets the trusted anchors. - /// - /// - /// A trusted anchor is a trusted root-level X.509 certificate, - /// generally issued by a Certificate Authority (CA). - /// - /// The trusted anchors. - protected override Org.BouncyCastle.Utilities.Collections.HashSet GetTrustedAnchors () - { - var anchors = new Org.BouncyCastle.Utilities.Collections.HashSet (); - var selector = new X509CertStoreSelector (); - var keyUsage = new bool[9]; - - keyUsage[(int) X509KeyUsageBits.KeyCertSign] = true; - selector.KeyUsage = keyUsage; - - foreach (var record in dbase.Find (selector, true, X509CertificateRecordFields.Certificate)) - anchors.Add (new TrustAnchor (record.Certificate, null)); - - return anchors; - } - - /// - /// Gets the intermediate certificates. - /// - /// - /// An intermediate certificate is any certificate that exists between the root - /// certificate issued by a Certificate Authority (CA) and the certificate at - /// the end of the chain. - /// - /// The intermediate certificates. - protected override IX509Store GetIntermediateCertificates () - { - //var intermediates = new X509CertificateStore (); - //var selector = new X509CertStoreSelector (); - //var keyUsage = new bool[9]; - - //keyUsage[(int) X509KeyUsageBits.KeyCertSign] = true; - //selector.KeyUsage = keyUsage; - - //foreach (var record in dbase.Find (selector, false, X509CertificateRecordFields.Certificate)) { - // if (!record.Certificate.IsSelfSigned ()) - // intermediates.Add (record.Certificate); - //} - - //return intermediates; - return dbase; - } - - /// - /// Gets the certificate revocation lists. - /// - /// - /// A Certificate Revocation List (CRL) is a list of certificate serial numbers issued - /// by a particular Certificate Authority (CA) that have been revoked, either by the CA - /// itself or by the owner of the revoked certificate. - /// - /// The certificate revocation lists. - protected override IX509Store GetCertificateRevocationLists () - { - return dbase.GetCrlStore (); - } - - /// - /// Get the date & time for the next scheduled certificate revocation list update for the specified issuer. - /// - /// - /// Gets the date & time for the next scheduled certificate revocation list update for the specified issuer. - /// - /// The date & time for the next update (in UTC). - /// The issuer. - protected override DateTime GetNextCertificateRevocationListUpdate (X509Name issuer) - { - var nextUpdate = DateTime.MinValue.ToUniversalTime (); - - foreach (var record in dbase.Find (issuer, X509CrlRecordFields.NextUpdate)) - nextUpdate = record.NextUpdate > nextUpdate ? record.NextUpdate : nextUpdate; - - return nextUpdate; - } - - /// - /// Gets the for the specified mailbox. - /// - /// - /// Constructs a with the appropriate certificate and - /// for the specified mailbox. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address for database lookups. - /// - /// A . - /// The recipient's mailbox address. - /// - /// A certificate for the specified could not be found. - /// - protected override CmsRecipient GetCmsRecipient (MailboxAddress mailbox) - { - foreach (var record in dbase.Find (mailbox, DateTime.UtcNow, false, CmsRecipientFields)) { - if (record.KeyUsage != 0 && (record.KeyUsage & X509KeyUsageFlags.KeyEncipherment) == 0) - continue; - - var recipient = new CmsRecipient (record.Certificate); - - if (record.Algorithms != null) - recipient.EncryptionAlgorithms = record.Algorithms; - - return recipient; - } - - throw new CertificateNotFoundException (mailbox, "A valid certificate could not be found."); - } - - /// - /// Gets the for the specified mailbox. - /// - /// - /// Constructs a with the appropriate signing certificate - /// for the specified mailbox. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address for database lookups. - /// - /// A . - /// The signer's mailbox address. - /// The preferred digest algorithm. - /// - /// A certificate for the specified could not be found. - /// - protected override CmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgorithm digestAlgo) - { - AsymmetricKeyParameter privateKey = null; - X509Certificate certificate = null; - - foreach (var record in dbase.Find (mailbox, DateTime.UtcNow, true, CmsSignerFields)) { - if (record.KeyUsage != X509KeyUsageFlags.None && (record.KeyUsage & DigitalSignatureKeyUsageFlags) == 0) - continue; - - certificate = record.Certificate; - privateKey = record.PrivateKey; - break; - } - - if (certificate != null && privateKey != null) { - var signer = new CmsSigner (BuildCertificateChain (certificate), privateKey); - signer.DigestAlgorithm = digestAlgo; - - return signer; - } - - throw new CertificateNotFoundException (mailbox, "A valid signing certificate could not be found."); - } - - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// - /// The certificate. - /// The encryption algorithm capabilities of the client (in preferred order). - /// The timestamp in coordinated universal time (UTC). - protected override void UpdateSecureMimeCapabilities (X509Certificate certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp) - { - X509CertificateRecord record; - - if ((record = dbase.Find (certificate, AlgorithmFields)) == null) { - record = new X509CertificateRecord (certificate); - record.AlgorithmsUpdated = timestamp; - record.Algorithms = algorithms; - - dbase.Add (record); - } else if (timestamp > record.AlgorithmsUpdated) { - record.AlgorithmsUpdated = timestamp; - record.Algorithms = algorithms; - - dbase.Update (record, AlgorithmFields); - } - } - - /// - /// Imports a certificate. - /// - /// - /// Imports the specified certificate into the database. - /// - /// The certificate. - /// - /// is null. - /// - public override void Import (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - if (dbase.Find (certificate, X509CertificateRecordFields.Id) == null) - dbase.Add (new X509CertificateRecord (certificate)); - } - - /// - /// Imports a certificate revocation list. - /// - /// - /// Imports the specified certificate revocation list. - /// - /// The certificate revocation list. - /// - /// is null. - /// - public override void Import (X509Crl crl) - { - if (crl == null) - throw new ArgumentNullException (nameof (crl)); - - // check for an exact match... - if (dbase.Find (crl, X509CrlRecordFields.Id) != null) - return; - - const X509CrlRecordFields fields = ~X509CrlRecordFields.Crl; - var obsolete = new List (); - var delta = crl.IsDelta (); - - // scan over our list of CRLs by the same issuer to check if this CRL obsoletes any - // older CRLs or if there are any newer CRLs that obsolete that obsolete this one. - foreach (var record in dbase.Find (crl.IssuerDN, fields)) { - if (!record.IsDelta && record.ThisUpdate >= crl.ThisUpdate) { - // we have a complete CRL that obsoletes this CRL - return; - } - - if (!delta) - obsolete.Add (record); - } - - // remove any obsoleted CRLs - foreach (var record in obsolete) - dbase.Remove (record); - - dbase.Add (new X509CrlRecord (crl)); - } - - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// - /// Imports all of the certificates and keys from the pkcs12-encoded stream. - /// - /// The raw certificate and key data. - /// The password to unlock the data. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override void Import (Stream stream, string password) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ()); - var enabledAlgorithms = EnabledEncryptionAlgorithms; - X509CertificateRecord record; - - foreach (string alias in pkcs12.Aliases) { - if (pkcs12.IsKeyEntry (alias)) { - var chain = pkcs12.GetCertificateChain (alias); - var entry = pkcs12.GetKey (alias); - int startIndex = 0; - - if (entry.Key.IsPrivate) { - if ((record = dbase.Find (chain[0].Certificate, ImportPkcs12Fields)) == null) { - record = new X509CertificateRecord (chain[0].Certificate, entry.Key); - record.AlgorithmsUpdated = DateTime.UtcNow; - record.Algorithms = enabledAlgorithms; - record.IsTrusted = true; - dbase.Add (record); - } else { - record.AlgorithmsUpdated = DateTime.UtcNow; - record.Algorithms = enabledAlgorithms; - if (record.PrivateKey == null) - record.PrivateKey = entry.Key; - record.IsTrusted = true; - dbase.Update (record, ImportPkcs12Fields); - } - - startIndex = 1; - } - - for (int i = startIndex; i < chain.Length; i++) - Import (chain[i].Certificate, true); - } else if (pkcs12.IsCertificateEntry (alias)) { - var entry = pkcs12.GetCertificate (alias); - - Import (entry.Certificate, true); - } - } - } - - #endregion - - /// - /// Imports a certificate. - /// - /// - /// Imports the certificate. - /// If the certificate already exists in the database and is true, - /// then the IsTrusted state is updated otherwise the certificate is added to the database with the - /// specified trust. - /// - /// The certificate. - /// true if the certificate is trusted; otherwise, false. - /// - /// is null. - /// - public void Import (X509Certificate certificate, bool trusted) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - X509CertificateRecord record; - - if ((record = dbase.Find (certificate, X509CertificateRecordFields.Id | X509CertificateRecordFields.Trusted)) != null) { - if (trusted && !record.IsTrusted) { - record.IsTrusted = trusted; - dbase.Update (record, X509CertificateRecordFields.Trusted); - } - - return; - } - - record = new X509CertificateRecord (certificate); - record.IsTrusted = trusted; - dbase.Add (record); - } - - /// - /// Imports a DER-encoded certificate stream. - /// - /// - /// Imports the certificate(s). - /// - /// The raw certificate(s). - /// true if the certificates are trusted; othewrwise, false. - /// - /// is null. - /// - public void Import (Stream stream, bool trusted) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new X509CertificateParser (); - - foreach (X509Certificate certificate in parser.ReadCertificates (stream)) - Import (certificate, trusted); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - dbase.Dispose (); - - base.Dispose (disposing); - } - } -} diff --git a/src/MimeKit/Cryptography/DigestAlgorithm.cs b/src/MimeKit/Cryptography/DigestAlgorithm.cs deleted file mode 100644 index 154f73a..0000000 --- a/src/MimeKit/Cryptography/DigestAlgorithm.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// DigestAlgorithm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// A digest algorithm. - /// - /// - /// Digest algorithms are secure hashing algorithms that are used - /// to generate unique fixed-length signatures for arbitrary data. - /// The most commonly used digest algorithms are currently MD5 - /// and SHA-1, however, MD5 was successfully broken in 2008 and should - /// be avoided. In late 2013, Microsoft announced that they would be - /// retiring their use of SHA-1 in their products by 2016 with the - /// assumption that its days as an unbroken digest algorithm were - /// numbered. It is speculated that the SHA-1 digest algorithm will - /// be vulnerable to collisions, and thus no longer considered secure, - /// by 2018. - /// Microsoft and other vendors plan to move to the SHA-2 suite of - /// digest algorithms which includes the following 4 variants: SHA-224, - /// SHA-256, SHA-384, and SHA-512. - /// - public enum DigestAlgorithm { - /// - /// No digest algorithm specified. - /// - None = 0, - - /// - /// The MD5 digest algorithm. - /// - MD5 = 1, - - /// - /// The SHA-1 digest algorithm. - /// - Sha1 = 2, - - /// - /// The Ripe-MD/160 digest algorithm. - /// - RipeMD160 = 3, - - /// - /// The double-SHA digest algorithm. - /// - DoubleSha = 4, - - /// - /// The MD2 digest algorithm. - /// - MD2 = 5, - - /// - /// The TIGER/192 digest algorithm. - /// - Tiger192 = 6, - - /// - /// The HAVAL 5-pass 160-bit digest algorithm. - /// - Haval5160 = 7, - - /// - /// The SHA-256 digest algorithm. - /// - Sha256 = 8, - - /// - /// The SHA-384 digest algorithm. - /// - Sha384 = 9, - - /// - /// The SHA-512 digest algorithm. - /// - Sha512 = 10, - - /// - /// The SHA-224 digest algorithm. - /// - Sha224 = 11, - - /// - /// The MD4 digest algorithm. - /// - MD4 = 301 - } -} diff --git a/src/MimeKit/Cryptography/DigitalSignatureCollection.cs b/src/MimeKit/Cryptography/DigitalSignatureCollection.cs deleted file mode 100644 index 211dfe4..0000000 --- a/src/MimeKit/Cryptography/DigitalSignatureCollection.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// DigitalSignatureCollection.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace MimeKit.Cryptography { - /// - /// A collection of digital signatures. - /// - /// - /// When verifying a digitally signed MIME part such as a - /// or a , you will get back a collection of - /// digital signatures. Typically, a signed message will only have a single signature - /// (created by the sender of the message), but it is possible for there to be - /// multiple signatures. - /// - public class DigitalSignatureCollection : ReadOnlyCollection - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The signatures. - public DigitalSignatureCollection (IList signatures) : base (signatures) - { - } - } -} diff --git a/src/MimeKit/Cryptography/DigitalSignatureVerifyException.cs b/src/MimeKit/Cryptography/DigitalSignatureVerifyException.cs deleted file mode 100644 index cd491eb..0000000 --- a/src/MimeKit/Cryptography/DigitalSignatureVerifyException.cs +++ /dev/null @@ -1,147 +0,0 @@ -// -// DigitalSignatureVerifyException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MimeKit.Cryptography { - /// - /// An exception that is thrown when an error occurrs in . - /// - /// - /// For more information about the error condition, check the property. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class DigitalSignatureVerifyException : Exception - { -#if SERIALIZABLE - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The serialization info. - /// The stream context. - /// - /// is null. - /// - protected DigitalSignatureVerifyException (SerializationInfo info, StreamingContext context) : base (info, context) - { - KeyId = (long?) info.GetValue ("KeyId", typeof (long?)); - } -#endif - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The key identifier. - /// The error message. - /// The inner exception. - public DigitalSignatureVerifyException (long keyId, string message, Exception innerException) : base (message, innerException) - { - KeyId = keyId; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The key identifier. - /// The error message. - public DigitalSignatureVerifyException (long keyId, string message) : base (message) - { - KeyId = keyId; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The inner exception. - public DigitalSignatureVerifyException (string message, Exception innerException) : base (message, innerException) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - public DigitalSignatureVerifyException (string message) : base (message) - { - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Sets the - /// with information about the exception. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("KeyId", KeyId, typeof (long?)); - } -#endif - - /// - /// Get the key identifier, if available. - /// - /// - /// Gets the key identifier, if available. - /// - /// The key identifier. - public long? KeyId { - get; private set; - } - } -} diff --git a/src/MimeKit/Cryptography/DkimBodyFilter.cs b/src/MimeKit/Cryptography/DkimBodyFilter.cs deleted file mode 100644 index b675cc3..0000000 --- a/src/MimeKit/Cryptography/DkimBodyFilter.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -// DkimBodyFilterBase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MimeKit.IO.Filters; - -namespace MimeKit.Cryptography { - /// - /// A base implementation for DKIM body filters. - /// - /// - /// A base implementation for DKIM body filters. - /// - abstract class DkimBodyFilter : MimeFilterBase - { - /// - /// Get or set whether the last filtered character was a newline. - /// - /// - /// Gets or sets whether the last filtered character was a newline. - /// - internal protected bool LastWasNewLine; - - /// - /// Get or set whether the current line is empty. - /// - /// - /// Gets or sets whether the current line is empty. - /// - protected bool IsEmptyLine; - - /// - /// Get or set the number of consecutive empty lines encountered. - /// - /// - /// Gets or sets the number of consecutive empty lines encountered. - /// - protected int EmptyLines; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - protected DkimBodyFilter () - { - } - } -} diff --git a/src/MimeKit/Cryptography/DkimCanonicalizationAlgorithm.cs b/src/MimeKit/Cryptography/DkimCanonicalizationAlgorithm.cs deleted file mode 100644 index 67f0f75..0000000 --- a/src/MimeKit/Cryptography/DkimCanonicalizationAlgorithm.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// DkimCanonicalizationAlgorithm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// A DKIM canonicalization algorithm. - /// - /// - /// Empirical evidence demonstrates that some mail servers and relay systems - /// modify email in transit, potentially invalidating a signature. There are two - /// competing perspectives on such modifications. For most signers, mild modification - /// of email is immaterial to the authentication status of the email. For such signers, - /// a canonicalization algorithm that survives modest in-transit modification is - /// preferred. - /// Other signers demand that any modification of the email, however minor, - /// result in a signature verification failure. These signers prefer a canonicalization - /// algorithm that does not tolerate in-transit modification of the signed email. - /// - /// - /// - /// - public enum DkimCanonicalizationAlgorithm { - /// - /// The simple canonicalization algorithm tolerates almost no modification - /// by mail servers while the message is in-transit. - /// - Simple, - - /// - /// The relaxed canonicalization algorithm tolerates common modifications - /// by mail servers while the message is in-transit such as whitespace - /// replacement and header field line rewrapping. - /// - Relaxed - } -} diff --git a/src/MimeKit/Cryptography/DkimHashStream.cs b/src/MimeKit/Cryptography/DkimHashStream.cs deleted file mode 100644 index b639b74..0000000 --- a/src/MimeKit/Cryptography/DkimHashStream.cs +++ /dev/null @@ -1,370 +0,0 @@ -// -// DkimHashStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; - -#if ENABLE_NATIVE_DKIM -using System.Security.Cryptography; -#else -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; -#endif - -namespace MimeKit.Cryptography { - /// - /// A DKIM hash stream. - /// - /// - /// A DKIM hash stream. - /// - class DkimHashStream : Stream - { -#if ENABLE_NATIVE_DKIM - HashAlgorithm digest; -#else - IDigest digest; -#endif - bool disposed; - int length; - int max; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The signature algorithm. - /// The max length of data to hash. - public DkimHashStream (DkimSignatureAlgorithm algorithm, int maxLength = -1) - { -#if ENABLE_NATIVE_DKIM - switch (algorithm) { - case DkimSignatureAlgorithm.Ed25519Sha256: - case DkimSignatureAlgorithm.RsaSha256: - digest = SHA256.Create (); - break; - default: - digest = SHA1.Create (); - break; - } -#else - switch (algorithm) { - case DkimSignatureAlgorithm.Ed25519Sha256: - case DkimSignatureAlgorithm.RsaSha256: - digest = new Sha256Digest (); - break; - default: - digest = new Sha1Digest (); - break; - } -#endif - - max = maxLength; - } - - /// - /// Generate the hash. - /// - /// - /// Generates the hash. - /// - /// The hash. - public byte[] GenerateHash () - { -#if ENABLE_NATIVE_DKIM - digest.TransformFinalBlock (new byte[0], 0, 0); - - return digest.Hash; -#else - var hash = new byte[digest.GetDigestSize ()]; - - digest.DoFinal (hash, 0); - - return hash; -#endif - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (DkimHashStream)); - } - - /// - /// Checks whether or not the stream supports reading. - /// - /// - /// A is not readable. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return false; } - } - - /// - /// Checks whether or not the stream supports writing. - /// - /// - /// A is always writable. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return true; } - } - - /// - /// Checks whether or not the stream supports seeking. - /// - /// - /// A is not seekable. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return false; } - } - - /// - /// Checks whether or not reading and writing to the stream can timeout. - /// - /// - /// Writing to a cannot timeout. - /// - /// true if reading and writing to the stream can timeout; otherwise, false. - public override bool CanTimeout { - get { return false; } - } - - /// - /// Gets the length of the stream, in bytes. - /// - /// - /// The length of a indicates the - /// number of bytes that have been written to it. - /// - /// The length of the stream in bytes. - /// - /// The stream has been disposed. - /// - public override long Length { - get { - CheckDisposed (); - - return length; - } - } - - /// - /// Gets or sets the current position within the stream. - /// - /// - /// Since it is possible to seek within a , - /// it is possible that the position will not always be identical to the - /// length of the stream, but typically it will be. - /// - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return length; } - set { Seek (value, SeekOrigin.Begin); } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reading from a is not supported. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - public override int Read (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - throw new NotSupportedException ("The stream does not support reading"); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Increments the property by the number of bytes written. - /// If the updated position is greater than the current length of the stream, then - /// the property will be updated to be identical to the - /// position. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - int n = max >= 0 && length + count > max ? max - length : count; - -#if ENABLE_NATIVE_DKIM - digest.TransformBlock (buffer, offset, count, null, 0); -#else - digest.BlockUpdate (buffer, offset, n); -#endif - - length += n; - } - - /// - /// Sets the position within the current stream. - /// - /// - /// Updates the within the stream. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// is not a valid . - /// - /// - /// The stream has been disposed. - /// - public override long Seek (long offset, SeekOrigin origin) - { - CheckDisposed (); - - throw new NotSupportedException ("The stream does not support seeking."); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Since a does not actually do anything other than - /// count bytes, this method is a no-op. - /// - /// - /// The stream has been disposed. - /// - public override void Flush () - { - CheckDisposed (); - - // nothing to do... - } - - /// - /// Sets the length of the stream. - /// - /// - /// Sets the to the specified value and updates - /// to the specified value if (and only if) - /// the current position is greater than the new length value. - /// - /// The desired length of the stream in bytes. - /// - /// is out of range. - /// - /// - /// The stream has been disposed. - /// - public override void SetLength (long value) - { - CheckDisposed (); - - throw new NotSupportedException ("The stream does not support setting the length."); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { -#if ENABLE_NATIVE_DKIM - digest.Dispose (); -#endif - - disposed = true; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MimeKit/Cryptography/DkimPublicKeyLocatorBase.cs b/src/MimeKit/Cryptography/DkimPublicKeyLocatorBase.cs deleted file mode 100644 index 3edc6a9..0000000 --- a/src/MimeKit/Cryptography/DkimPublicKeyLocatorBase.cs +++ /dev/null @@ -1,189 +0,0 @@ -// -// DkimPublicKeyLocatorBase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.OpenSsl; -using Org.BouncyCastle.Crypto.Parameters; - -namespace MimeKit.Cryptography { - /// - /// A base class for implemnentations of . - /// - /// - /// The class provides a helpful - /// method for parsing DNS TXT records in order to extract the public key. - /// - /// - /// - /// - /// - /// - /// - public abstract class DkimPublicKeyLocatorBase : IDkimPublicKeyLocator - { - /// - /// Get the public key from a DNS TXT record. - /// - /// - /// Gets the public key from a DNS TXT record. - /// - /// The DNS TXT record. - /// The public key. - /// - /// The is null. - /// - /// - /// There was an error parsing the DNS TXT record. - /// - protected AsymmetricKeyParameter GetPublicKey (string txt) - { - AsymmetricKeyParameter pubkey; - string k = null, p = null; - int index = 0; - - if (txt == null) - throw new ArgumentNullException (nameof (txt)); - - // parse the response (will look something like: "k=rsa; p=") - while (index < txt.Length) { - while (index < txt.Length && char.IsWhiteSpace (txt[index])) - index++; - - if (index == txt.Length) - break; - - // find the end of the key - int startIndex = index; - while (index < txt.Length && txt[index] != '=') - index++; - - if (index == txt.Length) - break; - - var key = txt.Substring (startIndex, index - startIndex); - - // skip over the '=' - index++; - - // find the end of the value - startIndex = index; - while (index < txt.Length && txt[index] != ';') - index++; - - var value = txt.Substring (startIndex, index - startIndex); - - switch (key) { - case "k": - switch (value) { - case "rsa": case "ed25519": k = value; break; - default: throw new ParseException ($"Unknown public key algorithm: {value}", startIndex, index); - } - break; - case "p": - p = value.Replace (" ", ""); - break; - } - - // skip over the ';' - index++; - } - - if (k != null && p != null) { - if (k == "ed25519") { - var decoded = Convert.FromBase64String (p); - - return new Ed25519PublicKeyParameters (decoded, 0); - } - - var data = "-----BEGIN PUBLIC KEY-----\r\n" + p + "\r\n-----END PUBLIC KEY-----\r\n"; - var rawData = Encoding.ASCII.GetBytes (data); - - using (var stream = new MemoryStream (rawData, false)) { - using (var reader = new StreamReader (stream)) { - var pem = new PemReader (reader); - - pubkey = pem.ReadObject () as AsymmetricKeyParameter; - - if (pubkey != null) - return pubkey; - } - } - } - - throw new ParseException ("Public key parameters not found in DNS TXT record.", 0, txt.Length); - } - - /// - /// Locate and retrieve the public key for the given domain and selector. - /// - /// - /// Locates and retrieves the public key for the given domain and selector. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// The public key. - /// A colon-separated list of query methods used to retrieve the public key. The default is "dns/txt". - /// The domain. - /// The selector. - /// The cancellation token. - public abstract AsymmetricKeyParameter LocatePublicKey (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously locate and retrieve the public key for the given domain and selector. - /// - /// - /// Locates and retrieves the public key for the given domain and selector. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// The public key. - /// A colon-separated list of query methods used to retrieve the public key. The default is "dns/txt". - /// The domain. - /// The selector. - /// The cancellation token. - public abstract Task LocatePublicKeyAsync (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MimeKit/Cryptography/DkimRelaxedBodyFilter.cs b/src/MimeKit/Cryptography/DkimRelaxedBodyFilter.cs deleted file mode 100644 index 91905ac..0000000 --- a/src/MimeKit/Cryptography/DkimRelaxedBodyFilter.cs +++ /dev/null @@ -1,167 +0,0 @@ -// -// DkimRelaxedBodyFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MimeKit.Utils; - -namespace MimeKit.Cryptography { - /// - /// A filter for the DKIM relaxed body canonicalization. - /// - /// - /// A filter for the DKIM relaxed body canonicalization. - /// - class DkimRelaxedBodyFilter : DkimBodyFilter - { - bool lwsp, cr; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public DkimRelaxedBodyFilter () - { - LastWasNewLine = true; - IsEmptyLine = true; - } - - unsafe int Filter (byte* inbuf, int length, byte* outbuf) - { - byte* inend = inbuf + length; - byte* outptr = outbuf; - byte* inptr = inbuf; - int count = 0; - - while (inptr < inend) { - if (*inptr == (byte) '\n') { - if (IsEmptyLine) { - EmptyLines++; - } else { - if (cr) { - *outptr++ = (byte) '\r'; - count++; - } - - *outptr++ = (byte) '\n'; - LastWasNewLine = true; - IsEmptyLine = true; - count++; - } - - lwsp = false; - cr = false; - } else { - if (cr) { - *outptr++ = (byte) '\r'; - cr = false; - count++; - } - - if (*inptr == (byte) '\r') { - lwsp = false; - cr = true; - } else if ((*inptr).IsBlank ()) { - lwsp = true; - } else { - if (EmptyLines > 0) { - // unwind our collection of empty lines - while (EmptyLines > 0) { - *outptr++ = (byte) '\r'; - *outptr++ = (byte) '\n'; - EmptyLines--; - count += 2; - } - } - - if (lwsp) { - // collapse lwsp to a single space - *outptr++ = (byte) ' '; - lwsp = false; - count++; - } - - LastWasNewLine = false; - IsEmptyLine = false; - - *outptr++ = *inptr; - count++; - } - } - - inptr++; - } - - return count; - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - EnsureOutputSize (length + (lwsp ? 1 : 0) + (EmptyLines * 2) + (cr ? 1 : 0) + 1, false); - - unsafe { - fixed (byte* inptr = input, outptr = OutputBuffer) { - outputLength = Filter (inptr + startIndex, length, outptr); - } - } - - outputIndex = 0; - - return OutputBuffer; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - LastWasNewLine = true; - IsEmptyLine = true; - EmptyLines = 0; - lwsp = false; - cr = false; - - base.Reset (); - } - } -} diff --git a/src/MimeKit/Cryptography/DkimSignatureAlgorithm.cs b/src/MimeKit/Cryptography/DkimSignatureAlgorithm.cs deleted file mode 100644 index 5b38ddc..0000000 --- a/src/MimeKit/Cryptography/DkimSignatureAlgorithm.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// DkimSignatureAlgorithm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// A DKIM signature algorithm. - /// - /// - /// A DKIM signature algorithm. - /// - /// - /// - /// - public enum DkimSignatureAlgorithm { - /// - /// The RSA-SHA1 signature algorithm. - /// - RsaSha1, - - /// - /// The RSA-SHA256 signature algorithm. - /// - RsaSha256, - - /// - /// The Ed25519-SHA256 signature algorithm. - /// - Ed25519Sha256 - } -} diff --git a/src/MimeKit/Cryptography/DkimSignatureStream.cs b/src/MimeKit/Cryptography/DkimSignatureStream.cs deleted file mode 100644 index 83badb5..0000000 --- a/src/MimeKit/Cryptography/DkimSignatureStream.cs +++ /dev/null @@ -1,361 +0,0 @@ -// -// DkimSignatureStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; - -using Org.BouncyCastle.Crypto; - -namespace MimeKit.Cryptography { - /// - /// A DKIM signature stream. - /// - /// - /// A DKIM signature stream. - /// - class DkimSignatureStream : Stream - { - bool disposed; - long length; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The digest signer. - /// - /// is null. - /// - public DkimSignatureStream (ISigner signer) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - Signer = signer; - } - - /// - /// Get the digest signer. - /// - /// - /// Gets the digest signer. - /// - /// The signer. - public ISigner Signer { - get; private set; - } - - /// - /// Generate the signature. - /// - /// - /// Generates the signature. - /// - /// The signature. - public byte[] GenerateSignature () - { - return Signer.GenerateSignature (); - } - - /// - /// Verify the DKIM signature. - /// - /// - /// Verifies the DKIM signature. - /// - /// true if signature is valid; otherwise, false. - /// The base64 encoded DKIM signature from the b= parameter. - /// - /// is null. - /// - public bool VerifySignature (string signature) - { - if (signature == null) - throw new ArgumentNullException (nameof (signature)); - - var rawSignature = Convert.FromBase64String (signature); - - return Signer.VerifySignature (rawSignature); - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (DkimSignatureStream)); - } - - /// - /// Checks whether or not the stream supports reading. - /// - /// - /// A is not readable. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return false; } - } - - /// - /// Checks whether or not the stream supports writing. - /// - /// - /// A is always writable. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return true; } - } - - /// - /// Checks whether or not the stream supports seeking. - /// - /// - /// A is not seekable. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return false; } - } - - /// - /// Checks whether or not reading and writing to the stream can timeout. - /// - /// - /// Writing to a cannot timeout. - /// - /// true if reading and writing to the stream can timeout; otherwise, false. - public override bool CanTimeout { - get { return false; } - } - - /// - /// Gets the length of the stream, in bytes. - /// - /// - /// The length of a indicates the - /// number of bytes that have been written to it. - /// - /// The length of the stream in bytes. - /// - /// The stream has been disposed. - /// - public override long Length { - get { - CheckDisposed (); - - return length; - } - } - - /// - /// Gets or sets the current position within the stream. - /// - /// - /// Since it is possible to seek within a , - /// it is possible that the position will not always be identical to the - /// length of the stream, but typically it will be. - /// - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return length; } - set { Seek (value, SeekOrigin.Begin); } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reading from a is not supported. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - public override int Read (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - throw new NotSupportedException ("The stream does not support reading"); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Increments the property by the number of bytes written. - /// If the updated position is greater than the current length of the stream, then - /// the property will be updated to be identical to the - /// position. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - Signer.BlockUpdate (buffer, offset, count); - - length += count; - } - - /// - /// Sets the position within the current stream. - /// - /// - /// Updates the within the stream. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// is not a valid . - /// - /// - /// The stream has been disposed. - /// - public override long Seek (long offset, SeekOrigin origin) - { - CheckDisposed (); - - throw new NotSupportedException ("The stream does not support seeking."); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Since a does not actually do anything other than - /// count bytes, this method is a no-op. - /// - /// - /// The stream has been disposed. - /// - public override void Flush () - { - CheckDisposed (); - - // nothing to do... - } - - /// - /// Sets the length of the stream. - /// - /// - /// Sets the to the specified value and updates - /// to the specified value if (and only if) - /// the current position is greater than the new length value. - /// - /// The desired length of the stream in bytes. - /// - /// is out of range. - /// - /// - /// The stream has been disposed. - /// - public override void SetLength (long value) - { - CheckDisposed (); - - throw new NotSupportedException ("The stream does not support setting the length."); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { -#if ENABLE_NATIVE_DKIM - var sss = Signer as SystemSecuritySigner; - - if (sss != null) - sss.Dispose (); -#endif - - disposed = true; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MimeKit/Cryptography/DkimSigner.cs b/src/MimeKit/Cryptography/DkimSigner.cs deleted file mode 100644 index 8b86833..0000000 --- a/src/MimeKit/Cryptography/DkimSigner.cs +++ /dev/null @@ -1,483 +0,0 @@ -// -// DkimSigner.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Collections.Generic; - -using Org.BouncyCastle.Crypto; - -using MimeKit.IO; -using MimeKit.Utils; - -namespace MimeKit.Cryptography { - /// - /// A DKIM signer. - /// - /// - /// A DKIM signer. - /// - /// - /// - /// - public class DkimSigner : DkimSignerBase - { - static readonly string[] DkimShouldNotInclude = { "return-path", "received", "comments", "keywords", "bcc", "resent-bcc", "dkim-signature" }; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be used. - /// - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// - protected DkimSigner (string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : base (domain, selector, algorithm) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be used. - /// - /// The signer's private key. - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a private key. - /// - public DkimSigner (AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (!key.IsPrivate) - throw new ArgumentException ("The key must be a private key.", nameof (key)); - - PrivateKey = key; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be used. - /// - /// - /// - /// - /// The file containing the private key. - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The file did not contain a private key. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An I/O error occurred. - /// - public DkimSigner (string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The file name cannot be empty.", nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - PrivateKey = LoadPrivateKey (stream); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be used. - /// - /// The stream containing the private key. - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The file did not contain a private key. - /// - /// - /// An I/O error occurred. - /// - public DkimSigner (Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) : this (domain, selector, algorithm) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - PrivateKey = LoadPrivateKey (stream); - } - - /// - /// Get or set the agent or user identifier. - /// - /// - /// Gets or sets the agent or user identifier. - /// - /// - /// - /// - /// The agent or user identifier. - public string AgentOrUserIdentifier { - get; set; - } - - /// - /// Get or set the public key query method. - /// - /// - /// Gets or sets the public key query method. - /// The value should be a colon-separated list of query methods used to - /// retrieve the public key (plain-text; OPTIONAL, default is "dns/txt"). Each - /// query method is of the form "type[/options]", where the syntax and - /// semantics of the options depend on the type and specified options. - /// - /// - /// - /// - /// The public key query method. - public string QueryMethod { - get; set; - } - - /// - /// Get the timestamp value. - /// - /// - /// Gets the timestamp to use as the t= value in the DKIM-Signature header. - /// - /// A value representing the timestamp value. - protected virtual long GetTimestamp () - { - return (long) (DateTime.UtcNow - DateUtils.UnixEpoch).TotalSeconds; - } - - void DkimSign (FormatOptions options, MimeMessage message, IList headers) - { - var value = new StringBuilder ("v=1"); - var t = GetTimestamp (); - byte[] signature, hash; - Header dkim; - - options = options.Clone (); - options.NewLineFormat = NewLineFormat.Dos; - options.EnsureNewLine = true; - - switch (SignatureAlgorithm) { - case DkimSignatureAlgorithm.Ed25519Sha256: - value.Append ("; a=ed25519-sha256"); - break; - case DkimSignatureAlgorithm.RsaSha256: - value.Append ("; a=rsa-sha256"); - break; - default: - value.Append ("; a=rsa-sha1"); - break; - } - - value.AppendFormat ("; d={0}; s={1}", Domain, Selector); - value.AppendFormat ("; c={0}/{1}", - HeaderCanonicalizationAlgorithm.ToString ().ToLowerInvariant (), - BodyCanonicalizationAlgorithm.ToString ().ToLowerInvariant ()); - if (!string.IsNullOrEmpty (QueryMethod)) - value.AppendFormat ("; q={0}", QueryMethod); - if (!string.IsNullOrEmpty (AgentOrUserIdentifier)) - value.AppendFormat ("; i={0}", AgentOrUserIdentifier); - value.AppendFormat ("; t={0}", t); - - using (var stream = new DkimSignatureStream (CreateSigningContext ())) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - // write the specified message headers - DkimVerifierBase.WriteHeaders (options, message, headers, HeaderCanonicalizationAlgorithm, filtered); - - value.AppendFormat ("; h={0}", string.Join (":", headers.ToArray ())); - - hash = message.HashBody (options, SignatureAlgorithm, BodyCanonicalizationAlgorithm, -1); - value.AppendFormat ("; bh={0}", Convert.ToBase64String (hash)); - value.Append ("; b="); - - dkim = new Header (HeaderId.DkimSignature, value.ToString ()); - message.Headers.Insert (0, dkim); - - switch (HeaderCanonicalizationAlgorithm) { - case DkimCanonicalizationAlgorithm.Relaxed: - DkimVerifierBase.WriteHeaderRelaxed (options, filtered, dkim, true); - break; - default: - DkimVerifierBase.WriteHeaderSimple (options, filtered, dkim, true); - break; - } - - filtered.Flush (); - } - - signature = stream.GenerateSignature (); - - dkim.Value += Convert.ToBase64String (signature); - } - } - - /// - /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// - /// - /// The formatting options. - /// The message to sign. - /// The list of header fields to sign. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - public void Sign (FormatOptions options, MimeMessage message, IList headers) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (headers == null) - throw new ArgumentNullException (nameof (headers)); - - var fields = new string[headers.Count]; - var containsFrom = false; - - for (int i = 0; i < headers.Count; i++) { - if (headers[i] == null) - throw new ArgumentException ("The list of headers cannot contain null.", nameof (headers)); - - if (headers[i].Length == 0) - throw new ArgumentException ("The list of headers cannot contain empty string.", nameof (headers)); - - fields[i] = headers[i].ToLowerInvariant (); - - if (DkimShouldNotInclude.Contains (fields[i])) - throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i]), nameof (headers)); - - if (fields[i] == "from") - containsFrom = true; - } - - if (!containsFrom) - throw new ArgumentException ("The list of headers to sign MUST include the 'From' header.", nameof (headers)); - - DkimSign (options, message, fields); - } - - /// - /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// - /// - /// The message to sign. - /// The headers to sign. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - public void Sign (MimeMessage message, IList headers) - { - Sign (FormatOptions.Default, message, headers); - } - - /// - /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// - /// - /// The formatting options. - /// The message to sign. - /// The list of header fields to sign. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - public void Sign (FormatOptions options, MimeMessage message, IList headers) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (headers == null) - throw new ArgumentNullException (nameof (headers)); - - var fields = new string[headers.Count]; - var containsFrom = false; - - for (int i = 0; i < headers.Count; i++) { - if (headers[i] == HeaderId.Unknown) - throw new ArgumentException ("The list of headers to sign cannot include the 'Unknown' header.", nameof (headers)); - - fields[i] = headers[i].ToHeaderName ().ToLowerInvariant (); - - if (DkimShouldNotInclude.Contains (fields[i])) - throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i].ToHeaderName ()), nameof (headers)); - - if (headers[i] == HeaderId.From) - containsFrom = true; - } - - if (!containsFrom) - throw new ArgumentException ("The list of headers to sign MUST include the 'From' header.", nameof (headers)); - - DkimSign (options, message, fields); - } - - /// - /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// - /// - /// The message to sign. - /// The headers to sign. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - public void Sign (MimeMessage message, IList headers) - { - Sign (FormatOptions.Default, message, headers); - } - } -} diff --git a/src/MimeKit/Cryptography/DkimSignerBase.cs b/src/MimeKit/Cryptography/DkimSignerBase.cs deleted file mode 100644 index a7f5257..0000000 --- a/src/MimeKit/Cryptography/DkimSignerBase.cs +++ /dev/null @@ -1,293 +0,0 @@ -// -// DkimSignerBase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -#if ENABLE_NATIVE_DKIM -using System.Security.Cryptography; -#endif - -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.OpenSsl; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Signers; - -namespace MimeKit.Cryptography { - /// - /// A base class for DKIM and ARC signers. - /// - /// - /// The base class for and . - /// - public abstract class DkimSignerBase - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be used. - /// - /// The domain that the signer represents. - /// The selector subdividing the domain. - /// The signature algorithm. - /// - /// is null. - /// -or- - /// is null. - /// - protected DkimSignerBase (string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) - { - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - if (selector == null) - throw new ArgumentNullException (nameof (selector)); - - SignatureAlgorithm = algorithm; - Selector = selector; - Domain = domain; - } - - /// - /// Get the domain that the signer represents. - /// - /// - /// Gets the domain that the signer represents. - /// - /// - /// - /// - /// The domain. - public string Domain { - get; private set; - } - - /// - /// Get the selector subdividing the domain. - /// - /// - /// Gets the selector subdividing the domain. - /// - /// - /// - /// - /// The selector. - public string Selector { - get; private set; - } - - /// - /// Get or set the algorithm to use for signing. - /// - /// - /// Gets or sets the algorithm to use for signing. - /// Creates a new . - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be used. - /// - /// - /// - /// - /// The signature algorithm. - public DkimSignatureAlgorithm SignatureAlgorithm { - get; set; - } - - /// - /// Get or set the canonicalization algorithm to use for the message body. - /// - /// - /// Gets or sets the canonicalization algorithm to use for the message body. - /// - /// - /// - /// - /// The canonicalization algorithm. - public DkimCanonicalizationAlgorithm BodyCanonicalizationAlgorithm { - get; set; - } - - /// - /// Get or set the canonicalization algorithm to use for the message headers. - /// - /// - /// Gets or sets the canonicalization algorithm to use for the message headers. - /// - /// - /// - /// - /// The canonicalization algorithm. - public DkimCanonicalizationAlgorithm HeaderCanonicalizationAlgorithm { - get; set; - } - - /// - /// Gets the private key. - /// - /// - /// The private key used for signing. - /// - /// The private key. - protected AsymmetricKeyParameter PrivateKey { - get; set; - } - - internal static AsymmetricKeyParameter LoadPrivateKey (Stream stream) - { - AsymmetricKeyParameter key = null; - - using (var reader = new StreamReader (stream)) { - var pem = new PemReader (reader); - - var keyObject = pem.ReadObject (); - - if (keyObject is AsymmetricCipherKeyPair pair) { - key = pair.Private; - } else if (keyObject is AsymmetricKeyParameter) { - key = (AsymmetricKeyParameter) keyObject; - } - } - - if (key == null || !key.IsPrivate) - throw new FormatException ("Private key not found."); - - return key; - } - - /// - /// Create the digest signing context. - /// - /// - /// Creates a new digest signing context. - /// - /// The digest signer. - /// - /// The is not supported. - /// - internal protected virtual ISigner CreateSigningContext () - { -#if ENABLE_NATIVE_DKIM - return new SystemSecuritySigner (SignatureAlgorithm, PrivateKey.AsAsymmetricAlgorithm ()); -#else - ISigner signer; - - switch (SignatureAlgorithm) { - case DkimSignatureAlgorithm.RsaSha1: - signer = new RsaDigestSigner (new Sha1Digest ()); - break; - case DkimSignatureAlgorithm.RsaSha256: - signer = new RsaDigestSigner (new Sha256Digest ()); - break; - case DkimSignatureAlgorithm.Ed25519Sha256: - signer = new Ed25519DigestSigner (new Sha256Digest ()); - break; - default: - throw new NotSupportedException (string.Format ("{0} is not supported.", SignatureAlgorithm)); - } - - signer.Init (true, PrivateKey); - - return signer; -#endif - } - } - -#if ENABLE_NATIVE_DKIM - class SystemSecuritySigner : ISigner - { - readonly RSACryptoServiceProvider rsa; - readonly HashAlgorithm hash; - readonly string oid; - - public SystemSecuritySigner (DkimSignatureAlgorithm algorithm, AsymmetricAlgorithm key) - { - rsa = key as RSACryptoServiceProvider; - - switch (algorithm) { - case DkimSignatureAlgorithm.RsaSha256: - oid = SecureMimeContext.GetDigestOid (DigestAlgorithm.Sha256); - AlgorithmName = "RSASHA256"; - hash = SHA256.Create (); - break; - default: - oid = SecureMimeContext.GetDigestOid (DigestAlgorithm.Sha1); - AlgorithmName = "RSASHA1"; - hash = SHA1.Create (); - break; - } - } - - public string AlgorithmName { - get; private set; - } - - public void BlockUpdate (byte[] input, int inOff, int length) - { - hash.TransformBlock (input, inOff, length, null, 0); - } - - public byte[] GenerateSignature () - { - hash.TransformFinalBlock (new byte[0], 0, 0); - - return rsa.SignHash (hash.Hash, oid); - } - - public void Init (bool forSigning, ICipherParameters parameters) - { - throw new NotImplementedException (); - } - - public void Reset () - { - hash.Initialize (); - } - - public void Update (byte input) - { - hash.TransformBlock (new byte[] { input }, 0, 1, null, 0); - } - - public bool VerifySignature (byte[] signature) - { - hash.TransformFinalBlock (new byte[0], 0, 0); - - return rsa.VerifyHash (hash.Hash, oid, signature); - } - - public void Dispose () - { - rsa.Dispose (); - } - } -#endif -} diff --git a/src/MimeKit/Cryptography/DkimSimpleBodyFilter.cs b/src/MimeKit/Cryptography/DkimSimpleBodyFilter.cs deleted file mode 100644 index 1c1e440..0000000 --- a/src/MimeKit/Cryptography/DkimSimpleBodyFilter.cs +++ /dev/null @@ -1,140 +0,0 @@ -// -// DkimSimpleBodyFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// A filter for the DKIM simple body canonicalization. - /// - /// - /// A filter for the DKIM simple body canonicalization. - /// - class DkimSimpleBodyFilter : DkimBodyFilter - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public DkimSimpleBodyFilter () - { - LastWasNewLine = false; - IsEmptyLine = true; - EmptyLines = 0; - } - - unsafe int Filter (byte* inbuf, int length, byte* outbuf) - { - byte* inend = inbuf + length; - byte* outptr = outbuf; - byte* inptr = inbuf; - int count = 0; - - while (inptr < inend) { - if (*inptr == (byte) '\r') { - if (!IsEmptyLine) { - *outptr++ = *inptr; - count++; - } - } else if (*inptr == (byte) '\n') { - if (!IsEmptyLine) { - *outptr++ = *inptr; - LastWasNewLine = true; - IsEmptyLine = true; - EmptyLines = 0; - count++; - } else { - EmptyLines++; - } - } else { - if (EmptyLines > 0) { - // unwind our collection of empty lines - while (EmptyLines > 0) { - *outptr++ = (byte) '\r'; - *outptr++ = (byte) '\n'; - EmptyLines--; - count += 2; - } - } - - LastWasNewLine = false; - IsEmptyLine = false; - - *outptr++ = *inptr; - count++; - } - - inptr++; - } - - return count; - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - EnsureOutputSize (length + EmptyLines * 2 + 1, false); - - unsafe { - fixed (byte* inptr = input, outptr = OutputBuffer) { - outputLength = Filter (inptr + startIndex, length, outptr); - } - } - - outputIndex = 0; - - return OutputBuffer; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - LastWasNewLine = false; - IsEmptyLine = true; - EmptyLines = 0; - - base.Reset (); - } - } -} diff --git a/src/MimeKit/Cryptography/DkimVerifier.cs b/src/MimeKit/Cryptography/DkimVerifier.cs deleted file mode 100644 index f45745e..0000000 --- a/src/MimeKit/Cryptography/DkimVerifier.cs +++ /dev/null @@ -1,308 +0,0 @@ -// -// DkimVerifier.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Parameters; - -using MimeKit.IO; - -namespace MimeKit.Cryptography { - /// - /// A DKIM-Signature verifier. - /// - /// - /// Verifies DomainKeys Identified Mail (DKIM) signatures. - /// - /// - /// - /// - public class DkimVerifier : DkimVerifierBase - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// - /// - /// - /// The public key locator. - /// - /// is null. - /// - public DkimVerifier (IDkimPublicKeyLocator publicKeyLocator) : base (publicKeyLocator) - { - } - - static void ValidateDkimSignatureParameters (IDictionary parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, - out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) - { - bool containsFrom = false; - - if (!parameters.TryGetValue ("v", out string v)) - throw new FormatException ("Malformed DKIM-Signature header: no version parameter detected."); - - if (v != "1") - throw new FormatException (string.Format ("Unrecognized DKIM-Signature version: v={0}", v)); - - ValidateCommonSignatureParameters ("DKIM-Signature", parameters, out algorithm, out headerAlgorithm, out bodyAlgorithm, out d, out s, out q, out headers, out bh, out b, out maxLength); - - for (int i = 0; i < headers.Length; i++) { - if (headers[i].Equals ("from", StringComparison.OrdinalIgnoreCase)) { - containsFrom = true; - break; - } - } - - if (!containsFrom) - throw new FormatException ("Malformed DKIM-Signature header: From header not signed."); - - if (parameters.TryGetValue ("i", out string id)) { - string ident; - int at; - - if ((at = id.LastIndexOf ('@')) == -1) - throw new FormatException ("Malformed DKIM-Signature header: no @ in the AUID value."); - - ident = id.Substring (at + 1); - - if (!ident.Equals (d, StringComparison.OrdinalIgnoreCase) && !ident.EndsWith ("." + d, StringComparison.OrdinalIgnoreCase)) - throw new FormatException ("Invalid DKIM-Signature header: the domain in the AUID does not match the domain parameter."); - } - } - - async Task VerifyAsync (FormatOptions options, MimeMessage message, Header dkimSignature, bool doAsync, CancellationToken cancellationToken) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (dkimSignature == null) - throw new ArgumentNullException (nameof (dkimSignature)); - - if (dkimSignature.Id != HeaderId.DkimSignature) - throw new ArgumentException ("The signature parameter MUST be a DKIM-Signature header.", nameof (dkimSignature)); - - var parameters = ParseParameterTags (dkimSignature.Id, dkimSignature.Value); - DkimCanonicalizationAlgorithm headerAlgorithm, bodyAlgorithm; - DkimSignatureAlgorithm signatureAlgorithm; - AsymmetricKeyParameter key; - string d, s, q, bh, b; - string[] headers; - int maxLength; - - ValidateDkimSignatureParameters (parameters, out signatureAlgorithm, out headerAlgorithm, out bodyAlgorithm, - out d, out s, out q, out headers, out bh, out b, out maxLength); - - if (!IsEnabled (signatureAlgorithm)) - return false; - - if (doAsync) - key = await PublicKeyLocator.LocatePublicKeyAsync (q, d, s, cancellationToken).ConfigureAwait (false); - else - key = PublicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); - - if ((key is RsaKeyParameters rsa) && rsa.Modulus.BitLength < MinimumRsaKeyLength) - return false; - - options = options.Clone (); - options.NewLineFormat = NewLineFormat.Dos; - - // first check the body hash (if that's invalid, then the entire signature is invalid) - var hash = Convert.ToBase64String (message.HashBody (options, signatureAlgorithm, bodyAlgorithm, maxLength)); - - if (hash != bh) - return false; - - using (var stream = new DkimSignatureStream (CreateVerifyContext (signatureAlgorithm, key))) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - WriteHeaders (options, message, headers, headerAlgorithm, filtered); - - // now include the DKIM-Signature header that we are verifying, - // but only after removing the "b=" signature value. - var header = GetSignedSignatureHeader (dkimSignature); - - switch (headerAlgorithm) { - case DkimCanonicalizationAlgorithm.Relaxed: - WriteHeaderRelaxed (options, filtered, header, true); - break; - default: - WriteHeaderSimple (options, filtered, header, true); - break; - } - - filtered.Flush (); - } - - return stream.VerifySignature (b); - } - } - - /// - /// Verify the specified DKIM-Signature header. - /// - /// - /// Verifies the specified DKIM-Signature header. - /// - /// - /// - /// - /// true if the DKIM-Signature is valid; otherwise, false. - /// The formatting options. - /// The message to verify. - /// The DKIM-Signature header. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a DKIM-Signature header. - /// - /// - /// The DKIM-Signature header value is malformed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public bool Verify (FormatOptions options, MimeMessage message, Header dkimSignature, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (options, message, dkimSignature, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously verify the specified DKIM-Signature header. - /// - /// - /// Verifies the specified DKIM-Signature header. - /// - /// - /// - /// - /// true if the DKIM-Signature is valid; otherwise, false. - /// The formatting options. - /// The message to verify. - /// The DKIM-Signature header. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a DKIM-Signature header. - /// - /// - /// The DKIM-Signature header value is malformed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public Task VerifyAsync (FormatOptions options, MimeMessage message, Header dkimSignature, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (options, message, dkimSignature, true, cancellationToken); - } - - /// - /// Verify the specified DKIM-Signature header. - /// - /// - /// Verifies the specified DKIM-Signature header. - /// - /// - /// - /// - /// true if the DKIM-Signature is valid; otherwise, false. - /// The message to verify. - /// The DKIM-Signature header. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a DKIM-Signature header. - /// - /// - /// The DKIM-Signature header value is malformed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public bool Verify (MimeMessage message, Header dkimSignature, CancellationToken cancellationToken = default (CancellationToken)) - { - return Verify (FormatOptions.Default, message, dkimSignature, cancellationToken); - } - - /// - /// Asynchronously verify the specified DKIM-Signature header. - /// - /// - /// Verifies the specified DKIM-Signature header. - /// - /// - /// - /// - /// true if the DKIM-Signature is valid; otherwise, false. - /// The message to verify. - /// The DKIM-Signature header. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a DKIM-Signature header. - /// - /// - /// The DKIM-Signature header value is malformed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - public Task VerifyAsync (MimeMessage message, Header dkimSignature, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (FormatOptions.Default, message, dkimSignature, cancellationToken); - } - } -} diff --git a/src/MimeKit/Cryptography/DkimVerifierBase.cs b/src/MimeKit/Cryptography/DkimVerifierBase.cs deleted file mode 100644 index 2528b61..0000000 --- a/src/MimeKit/Cryptography/DkimVerifierBase.cs +++ /dev/null @@ -1,495 +0,0 @@ -// -// DkimVerifierBase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Globalization; -using System.Collections.Generic; - -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Signers; - -using MimeKit.Utils; - -namespace MimeKit.Cryptography { - /// - /// A base class for DKIM and ARC verifiers. - /// - /// - /// The base class for and . - /// - public abstract class DkimVerifierBase - { - int enabledSignatureAlgorithms; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Initializes the . - /// - /// The public key locator. - /// - /// is null. - /// - protected DkimVerifierBase (IDkimPublicKeyLocator publicKeyLocator) - { - if (publicKeyLocator == null) - throw new ArgumentNullException (nameof (publicKeyLocator)); - - PublicKeyLocator = publicKeyLocator; - - Enable (DkimSignatureAlgorithm.Ed25519Sha256); - Enable (DkimSignatureAlgorithm.RsaSha256); - //Enable (DkimSignatureAlgorithm.RsaSha1); - MinimumRsaKeyLength = 1024; - } - - /// - /// Get the public key locator. - /// - /// - /// Gets the public key locator. - /// - /// The public key locator. - protected IDkimPublicKeyLocator PublicKeyLocator { - get; private set; - } - - /// - /// Get or set the minimum allowed RSA key length. - /// - /// - /// Gets the minimum allowed RSA key length. - /// The DKIM specifications specify a single signing algorithm, RSA, - /// and recommend key sizes of 1024 to 2048 bits (but require verification of 512-bit keys). - /// As discussed in US-CERT Vulnerability Note VU#268267, the operational community has - /// recognized that shorter keys compromise the effectiveness of DKIM. While 1024-bit - /// signatures are common, stronger signatures are not. Widely used DNS configuration - /// software places a practical limit on key sizes, because the software only handles a - /// single 256-octet string in a TXT record, and RSA keys significantly longer than 1024 - /// bits don't fit in 256 octets. - /// - public int MinimumRsaKeyLength { - get; set; - } - - /// - /// Enable a DKIM signature algorithm. - /// - /// - /// Enables the specified DKIM signature algorithm. - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be enabled. - /// - /// The DKIM signature algorithm. - public void Enable (DkimSignatureAlgorithm algorithm) - { - enabledSignatureAlgorithms |= 1 << (int) algorithm; - } - - /// - /// Disable a DKIM signature algorithm. - /// - /// - /// Disables the specified DKIM signature algorithm. - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be enabled. - /// - /// The DKIM signature algorithm. - public void Disable (DkimSignatureAlgorithm algorithm) - { - enabledSignatureAlgorithms &= ~(1 << (int) algorithm); - } - - /// - /// Check whether a DKIM signature algorithm is enabled. - /// - /// - /// Determines whether the specified DKIM signature algorithm is enabled. - /// Due to the recognized weakness of the SHA-1 hash algorithm - /// and the wide availability of the SHA-256 hash algorithm (it has been a required - /// part of DKIM since it was originally standardized in 2007), it is recommended - /// that NOT be enabled. - /// - /// true if the specified DKIM signature algorithm is enabled; otherwise, false. - /// The DKIM signature algorithm. - public bool IsEnabled (DkimSignatureAlgorithm algorithm) - { - return (enabledSignatureAlgorithms & (1 << (int) algorithm)) != 0; - } - - static bool IsWhiteSpace (char c) - { - return c == ' ' || c == '\t'; - } - - static bool IsAlpha (char c) - { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); - } - - internal static Dictionary ParseParameterTags (HeaderId header, string signature) - { - var parameters = new Dictionary (); - var value = new StringBuilder (); - int index = 0; - - while (index < signature.Length) { - while (index < signature.Length && IsWhiteSpace (signature[index])) - index++; - - if (index >= signature.Length) - break; - - if (signature[index] == ';' || !IsAlpha (signature[index])) - throw new FormatException (string.Format ("Malformed {0} value.", header.ToHeaderName ())); - - int startIndex = index++; - - while (index < signature.Length && signature[index] != '=') - index++; - - if (index >= signature.Length) - continue; - - var name = signature.Substring (startIndex, index - startIndex).TrimEnd (); - - // skip over '=' and clear value buffer - value.Length = 0; - index++; - - while (index < signature.Length && signature[index] != ';') { - if (!IsWhiteSpace (signature[index])) - value.Append (signature[index]); - index++; - } - - if (parameters.ContainsKey (name)) - throw new FormatException (string.Format ("Malformed {0} value: duplicate parameter '{1}'.", header.ToHeaderName (), name)); - - parameters.Add (name, value.ToString ()); - - // skip over ';' - index++; - } - - return parameters; - } - - internal static void ValidateCommonParameters (string header, IDictionary parameters, out DkimSignatureAlgorithm algorithm, - out string d, out string s, out string q, out string b) - { - if (!parameters.TryGetValue ("a", out string a)) - throw new FormatException (string.Format ("Malformed {0} header: no signature algorithm parameter detected.", header)); - - switch (a.ToLowerInvariant ()) { - case "ed25519-sha256": algorithm = DkimSignatureAlgorithm.Ed25519Sha256; break; - case "rsa-sha256": algorithm = DkimSignatureAlgorithm.RsaSha256; break; - case "rsa-sha1": algorithm = DkimSignatureAlgorithm.RsaSha1; break; - default: throw new FormatException (string.Format ("Unrecognized {0} algorithm parameter: a={1}", header, a)); - } - - if (!parameters.TryGetValue ("d", out d)) - throw new FormatException (string.Format ("Malformed {0} header: no domain parameter detected.", header)); - - if (d.Length == 0) - throw new FormatException (string.Format ("Malformed {0} header: empty domain parameter detected.", header)); - - if (!parameters.TryGetValue ("s", out s)) - throw new FormatException (string.Format ("Malformed {0} header: no selector parameter detected.", header)); - - if (s.Length == 0) - throw new FormatException (string.Format ("Malformed {0} header: empty selector parameter detected.", header)); - - if (!parameters.TryGetValue ("q", out q)) - q = "dns/txt"; - - if (!parameters.TryGetValue ("b", out b)) - throw new FormatException (string.Format ("Malformed {0} header: no signature parameter detected.", header)); - - if (b.Length == 0) - throw new FormatException (string.Format ("Malformed {0} header: empty signature parameter detected.", header)); - - if (parameters.TryGetValue ("t", out string t)) { - if (!int.TryParse (t, NumberStyles.Integer, CultureInfo.InvariantCulture, out int timestamp) || timestamp < 0) - throw new FormatException (string.Format ("Malformed {0} header: invalid timestamp parameter: t={1}.", header, t)); - } - } - - internal static void ValidateCommonSignatureParameters (string header, IDictionary parameters, out DkimSignatureAlgorithm algorithm, out DkimCanonicalizationAlgorithm headerAlgorithm, - out DkimCanonicalizationAlgorithm bodyAlgorithm, out string d, out string s, out string q, out string[] headers, out string bh, out string b, out int maxLength) - { - ValidateCommonParameters (header, parameters, out algorithm, out d, out s, out q, out b); - - if (parameters.TryGetValue ("l", out string l)) { - if (!int.TryParse (l, NumberStyles.Integer, CultureInfo.InvariantCulture, out maxLength) || maxLength < 0) - throw new FormatException (string.Format ("Malformed {0} header: invalid length parameter: l={1}", header, l)); - } else { - maxLength = -1; - } - - if (parameters.TryGetValue ("c", out string c)) { - var tokens = c.ToLowerInvariant ().Split ('/'); - - if (tokens.Length == 0 || tokens.Length > 2) - throw new FormatException (string.Format ("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); - - switch (tokens[0]) { - case "relaxed": headerAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; - case "simple": headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; - default: throw new FormatException (string.Format ("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); - } - - if (tokens.Length == 2) { - switch (tokens[1]) { - case "relaxed": bodyAlgorithm = DkimCanonicalizationAlgorithm.Relaxed; break; - case "simple": bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; break; - default: throw new FormatException (string.Format ("Malformed {0} header: invalid canonicalization parameter: c={1}", header, c)); - } - } else { - bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; - } - } else { - headerAlgorithm = DkimCanonicalizationAlgorithm.Simple; - bodyAlgorithm = DkimCanonicalizationAlgorithm.Simple; - } - - if (!parameters.TryGetValue ("h", out string h)) - throw new FormatException (string.Format ("Malformed {0} header: no signed header parameter detected.", header)); - - headers = h.Split (':'); - - if (!parameters.TryGetValue ("bh", out bh)) - throw new FormatException (string.Format ("Malformed {0} header: no body hash parameter detected.", header)); - } - - internal static void WriteHeaderRelaxed (FormatOptions options, Stream stream, Header header, bool isDkimSignature) - { - // o Convert all header field names (not the header field values) to - // lowercase. For example, convert "SUBJect: AbC" to "subject: AbC". - var name = Encoding.ASCII.GetBytes (header.Field.ToLowerInvariant ()); - var rawValue = header.GetRawValue (options); - int index = 0; - - // o Delete any WSP characters remaining before and after the colon - // separating the header field name from the header field value. The - // colon separator MUST be retained. - stream.Write (name, 0, name.Length); - stream.WriteByte ((byte) ':'); - - // trim leading whitespace... - while (index < rawValue.Length && rawValue[index].IsWhitespace ()) - index++; - - while (index < rawValue.Length) { - int startIndex = index; - - // look for the first non-whitespace character - while (index < rawValue.Length && rawValue[index].IsWhitespace ()) - index++; - - // o Delete all WSP characters at the end of each unfolded header field - // value. - if (index >= rawValue.Length) - break; - - // o Convert all sequences of one or more WSP characters to a single SP - // character. WSP characters here include those before and after a - // line folding boundary. - if (index > startIndex) - stream.WriteByte ((byte) ' '); - - startIndex = index; - - while (index < rawValue.Length && !rawValue[index].IsWhitespace ()) - index++; - - if (index > startIndex) - stream.Write (rawValue, startIndex, index - startIndex); - } - - if (!isDkimSignature) - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - - internal static void WriteHeaderSimple (FormatOptions options, Stream stream, Header header, bool isDkimSignature) - { - var rawValue = header.GetRawValue (options); - int rawLength = rawValue.Length; - - if (isDkimSignature && rawLength > 0) { - if (rawValue[rawLength - 1] == (byte) '\n') { - rawLength--; - - if (rawLength > 0 && rawValue[rawLength - 1] == (byte) '\r') - rawLength--; - } - } - - stream.Write (header.RawField, 0, header.RawField.Length); - stream.Write (Header.Colon, 0, Header.Colon.Length); - stream.Write (rawValue, 0, rawLength); - } - - /// - /// Create the digest signing context. - /// - /// - /// Creates a new digest signing context that uses the specified algorithm. - /// - /// The DKIM signature algorithm. - /// The public key. - /// The digest signer. - internal virtual ISigner CreateVerifyContext (DkimSignatureAlgorithm algorithm, AsymmetricKeyParameter key) - { -#if ENABLE_NATIVE_DKIM - return new SystemSecuritySigner (algorithm, key.AsAsymmetricAlgorithm ()); -#else - ISigner signer; - - switch (algorithm) { - case DkimSignatureAlgorithm.RsaSha1: - signer = new RsaDigestSigner (new Sha1Digest ()); - break; - case DkimSignatureAlgorithm.RsaSha256: - signer = new RsaDigestSigner (new Sha256Digest ()); - break; - case DkimSignatureAlgorithm.Ed25519Sha256: - signer = new Ed25519DigestSigner (new Sha256Digest ()); - break; - default: - throw new NotSupportedException (string.Format ("{0} is not supported.", algorithm)); - } - - signer.Init (key.IsPrivate, key); - - return signer; -#endif - } - - internal static void WriteHeaders (FormatOptions options, MimeMessage message, IList fields, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm, Stream stream) - { - var counts = new Dictionary (StringComparer.Ordinal); - - for (int i = 0; i < fields.Count; i++) { - var headers = fields[i].StartsWith ("Content-", StringComparison.OrdinalIgnoreCase) ? message.Body.Headers : message.Headers; - var name = fields[i].ToLowerInvariant (); - int index, count, n = 0; - - if (!counts.TryGetValue (name, out count)) - count = 0; - - // Note: signers choosing to sign an existing header field that occurs more - // than once in the message (such as Received) MUST sign the physically last - // instance of that header field in the header block. Signers wishing to sign - // multiple instances of such a header field MUST include the header field - // name multiple times in the list of header fields and MUST sign such header - // fields in order from the bottom of the header field block to the top. - index = headers.LastIndexOf (name); - - // find the n'th header with this name - while (n < count && --index >= 0) { - if (headers[index].Field.Equals (name, StringComparison.OrdinalIgnoreCase)) - n++; - } - - if (index < 0) - continue; - - var header = headers[index]; - - switch (headerCanonicalizationAlgorithm) { - case DkimCanonicalizationAlgorithm.Relaxed: - WriteHeaderRelaxed (options, stream, header, false); - break; - default: - WriteHeaderSimple (options, stream, header, false); - break; - } - - counts[name] = ++count; - } - } - - internal static Header GetSignedSignatureHeader (Header header) - { - // modify the raw DKIM-Signature header value by chopping off the signature value after the "b=" - var rawValue = (byte[]) header.RawValue.Clone (); - int length = 0, index = 0; - - do { - while (index < rawValue.Length && rawValue[index].IsWhitespace ()) - index++; - - if (index + 2 < rawValue.Length) { - var param = (char) rawValue[index++]; - - while (index < rawValue.Length && rawValue[index].IsWhitespace ()) - index++; - - if (index < rawValue.Length && rawValue[index] == (byte) '=' && param == 'b') { - length = ++index; - - while (index < rawValue.Length && rawValue[index] != (byte) ';') - index++; - - if (index == rawValue.Length && rawValue[index - 1] == (byte) '\n') { - index--; - - if (rawValue[index - 1] == (byte) '\r') - index--; - } - - break; - } - } - - while (index < rawValue.Length && rawValue[index] != (byte) ';') - index++; - - if (index < rawValue.Length) - index++; - } while (index < rawValue.Length); - - if (index == rawValue.Length) - throw new FormatException (string.Format ("Malformed {0} header: missing signature parameter.", header.Id.ToHeaderName ())); - - while (index < rawValue.Length) - rawValue[length++] = rawValue[index++]; - - Array.Resize (ref rawValue, length); - - return new Header (header.Options, header.RawField, rawValue, false); - } - } -} diff --git a/src/MimeKit/Cryptography/Ed25519DigestSigner.cs b/src/MimeKit/Cryptography/Ed25519DigestSigner.cs deleted file mode 100644 index 5f00ab5..0000000 --- a/src/MimeKit/Cryptography/Ed25519DigestSigner.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// Ed25519DigestSigner.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Math.EC.Rfc8032; -using Org.BouncyCastle.Crypto.Parameters; - -namespace MimeKit.Cryptography { - class Ed25519DigestSigner : ISigner - { - Ed25519PrivateKeyParameters privateKey; - Ed25519PublicKeyParameters publicKey; - readonly IDigest digest; - - public Ed25519DigestSigner (IDigest digest) - { - this.digest = digest; - } - - public string AlgorithmName { - get { return digest.AlgorithmName + "withEd25519"; } - } - - public void Init (bool forSigning, ICipherParameters parameters) - { - if (forSigning) { - privateKey = (Ed25519PrivateKeyParameters) parameters; - publicKey = privateKey.GeneratePublicKey (); - } else { - publicKey = (Ed25519PublicKeyParameters) parameters; - privateKey = null; - } - - Reset (); - } - - public void Update (byte input) - { - digest.Update (input); - } - - public void BlockUpdate (byte[] input, int inOff, int length) - { - digest.BlockUpdate (input, inOff, length); - } - - public byte[] GenerateSignature () - { - if (privateKey == null) - throw new InvalidOperationException ("Ed25519DigestSigner not initialised for signature generation."); - - var hash = new byte[digest.GetDigestSize ()]; - digest.DoFinal (hash, 0); - - var signature = new byte[Ed25519PrivateKeyParameters.SignatureSize]; - privateKey.Sign (Ed25519.Algorithm.Ed25519, publicKey, null, hash, 0, hash.Length, signature, 0); - - Reset (); - - return signature; - } - - public bool VerifySignature (byte[] signature) - { - if (privateKey != null) - throw new InvalidOperationException ("Ed25519DigestSigner not initialised for verification"); - - if (Ed25519.SignatureSize != signature.Length) - return false; - - byte[] hash = new byte[digest.GetDigestSize ()]; - digest.DoFinal (hash, 0); - - var pk = publicKey.GetEncoded (); - var result = Ed25519.Verify (signature, 0, pk, 0, hash, 0, hash.Length); - - Reset (); - - return result; - } - - public void Reset () - { - digest.Reset (); - } - } -} diff --git a/src/MimeKit/Cryptography/EncryptionAlgorithm.cs b/src/MimeKit/Cryptography/EncryptionAlgorithm.cs deleted file mode 100644 index cc092f2..0000000 --- a/src/MimeKit/Cryptography/EncryptionAlgorithm.cs +++ /dev/null @@ -1,142 +0,0 @@ -// -// EncryptionAlgorithm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// Encryption algorithms supported by S/MIME and OpenPGP. - /// - /// - /// Represents the available encryption algorithms for use with S/MIME and OpenPGP. - /// RC-2/40 was required by all S/MIME v2 implementations. However, since the - /// mid-to-late 1990's, RC-2/40 has been considered to be extremely weak and starting with - /// S/MIME v3.0 (published in 1999), all S/MIME implementations are required to implement - /// support for Triple-DES (aka 3DES) and should no longer encrypt using RC-2/40 unless - /// explicitly requested to do so by the user. - /// These days, most S/MIME implementations support the AES-128 and AES-256 - /// algorithms which are the recommended algorithms specified in S/MIME v3.2 and - /// should be preferred over the use of Triple-DES unless the client capabilities - /// of one or more of the recipients is unknown (or only supports Triple-DES). - /// - public enum EncryptionAlgorithm { - /// - /// The AES 128-bit encryption algorithm. - /// - Aes128, - - /// - /// The AES 192-bit encryption algorithm. - /// - Aes192, - - /// - /// The AES 256-bit encryption algorithm. - /// - Aes256, - - /// - /// The Camellia 128-bit encryption algorithm. - /// - Camellia128, - - /// - /// The Camellia 192-bit encryption algorithm. - /// - Camellia192, - - /// - /// The Camellia 256-bit encryption algorithm. - /// - Camellia256, - - /// - /// The Cast-5 128-bit encryption algorithm. - /// - Cast5, - - /// - /// The DES 56-bit encryption algorithm. - /// - /// - /// This is extremely weak encryption and should not be used - /// without consent from the user. - /// - Des, - - /// - /// The Triple-DES encryption algorithm. - /// - /// - /// This is the weakest recommended encryption algorithm for use - /// starting with S/MIME v3 and should only be used as a fallback - /// if it is unknown what encryption algorithms are supported by - /// the recipient's mail client. - /// - TripleDes, - - /// - /// The IDEA 128-bit encryption algorithm. - /// - Idea, - - /// - /// The Blowfish encryption algorithm. - /// - Blowfish, - - /// - /// The Twofish encryption algorithm. - /// - Twofish, - - /// - /// The RC2 40-bit encryption algorithm (S/MIME only). - /// - /// - /// This is extremely weak encryption and should not be used - /// without consent from the user. - /// - RC240, - - /// - /// The RC2 64-bit encryption algorithm (S/MIME only). - /// - /// - /// This is very weak encryption and should not be used - /// without consent from the user. - /// - RC264, - - /// - /// The RC2 128-bit encryption algorithm (S/MIME only). - /// - RC2128, - - /// - /// The SEED 128-bit encryption algorithm (S/MIME only). - /// - Seed - } -} diff --git a/src/MimeKit/Cryptography/GnuPGContext.cs b/src/MimeKit/Cryptography/GnuPGContext.cs deleted file mode 100644 index efbe928..0000000 --- a/src/MimeKit/Cryptography/GnuPGContext.cs +++ /dev/null @@ -1,268 +0,0 @@ -// -// GnuPGContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections.Generic; - -namespace MimeKit.Cryptography { - /// - /// A that uses the GnuPG keyrings. - /// - /// - /// A that uses the GnuPG keyrings. - /// - public abstract class GnuPGContext : OpenPgpContext - { - static readonly Dictionary EncryptionAlgorithms; - //static readonly Dictionary PublicKeyAlgorithms; - static readonly Dictionary DigestAlgorithms; - static readonly char[] Whitespace = { ' ', '\t' }; - static readonly string PublicKeyRing; - static readonly string SecretKeyRing; - static readonly string Configuration; - - static GnuPGContext () - { - var gnupg = Environment.GetEnvironmentVariable ("GNUPGHOME"); - - if (gnupg == null) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - if (Path.DirectorySeparatorChar == '\\') { - var appData = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); - gnupg = Path.Combine (appData, "gnupg"); - } else { - var home = Environment.GetFolderPath (Environment.SpecialFolder.Personal); - gnupg = Path.Combine (home, ".gnupg"); - } -#else - gnupg = ".gnupg"; -#endif - } - - PublicKeyRing = Path.Combine (gnupg, "pubring.gpg"); - SecretKeyRing = Path.Combine (gnupg, "secring.gpg"); - Configuration = Path.Combine (gnupg, "gpg.conf"); - - EncryptionAlgorithms = new Dictionary (StringComparer.Ordinal) { - { "AES", EncryptionAlgorithm.Aes128 }, - { "AES128", EncryptionAlgorithm.Aes128 }, - { "AES192", EncryptionAlgorithm.Aes192 }, - { "AES256", EncryptionAlgorithm.Aes256 }, - { "BLOWFISH", EncryptionAlgorithm.Blowfish }, - { "CAMELLIA128", EncryptionAlgorithm.Camellia128 }, - { "CAMELLIA192", EncryptionAlgorithm.Camellia192 }, - { "CAMELLIA256", EncryptionAlgorithm.Camellia256 }, - { "CAST5", EncryptionAlgorithm.Cast5 }, - { "IDEA", EncryptionAlgorithm.Idea }, - { "3DES", EncryptionAlgorithm.TripleDes }, - { "TWOFISH", EncryptionAlgorithm.Twofish } - }; - - //PublicKeyAlgorithms = new Dictionary { - // { "DSA", PublicKeyAlgorithm.Dsa }, - // { "ECDH", PublicKeyAlgorithm.EllipticCurve }, - // { "ECDSA", PublicKeyAlgorithm.EllipticCurveDsa }, - // { "EDDSA", PublicKeyAlgorithm.EdwardsCurveDsa }, - // { "ELG", PublicKeyAlgorithm.ElGamalGeneral }, - // { "RSA", PublicKeyAlgorithm.RsaGeneral } - //}; - - DigestAlgorithms = new Dictionary (StringComparer.Ordinal) { - { "RIPEMD160", DigestAlgorithm.RipeMD160 }, - { "SHA1", DigestAlgorithm.Sha1 }, - { "SHA224", DigestAlgorithm.Sha224 }, - { "SHA256", DigestAlgorithm.Sha256 }, - { "SHA384", DigestAlgorithm.Sha384 }, - { "SHA512", DigestAlgorithm.Sha512 } - }; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - protected GnuPGContext () : base (PublicKeyRing, SecretKeyRing) - { - LoadConfiguration (); - - foreach (var algorithm in EncryptionAlgorithmRank) - Enable (algorithm); - - foreach (var algorithm in DigestAlgorithmRank) - Enable (algorithm); - } - - void UpdateKeyServer (string value) - { - if (string.IsNullOrEmpty (value)) { - KeyServer = null; - return; - } - - if (!Uri.IsWellFormedUriString (value, UriKind.Absolute)) - return; - - KeyServer = new Uri (value, UriKind.Absolute); - } - - void UpdateKeyServerOptions (string value) - { - if (string.IsNullOrEmpty (value)) - return; - - var options = value.Split (Whitespace, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < options.Length; i++) { - switch (options[i]) { - case "auto-key-retrieve": - AutoKeyRetrieve = true; - break; - } - } - } - - static EncryptionAlgorithm[] ParseEncryptionAlgorithms (string value) - { - var names = value.Split (Whitespace, StringSplitOptions.RemoveEmptyEntries); - var algorithms = new List (); - var seen = new HashSet (); - - for (int i = 0; i < names.Length; i++) { - var name = names[i].ToUpperInvariant (); - EncryptionAlgorithm algorithm; - - if (EncryptionAlgorithms.TryGetValue (name, out algorithm) && seen.Add (algorithm)) - algorithms.Add (algorithm); - } - - if (!seen.Contains (EncryptionAlgorithm.TripleDes)) - algorithms.Add (EncryptionAlgorithm.TripleDes); - - return algorithms.ToArray (); - } - - //static PublicKeyAlgorithm[] ParsePublicKeyAlgorithms (string value) - //{ - // var names = value.Split (new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - // var algorithms = new List (); - // var seen = new HashSet (); - - // for (int i = 0; i < names.Length; i++) { - // var name = names[i].ToUpperInvariant (); - // PublicKeyAlgorithm algorithm; - - // if (PublicKeyAlgorithms.TryGetValue (name, out algorithm) && seen.Add (algorithm)) - // algorithms.Add (algorithm); - // } - - // if (!seen.Contains (PublicKeyAlgorithm.Dsa)) - // seen.Add (PublicKeyAlgorithm.Dsa); - - // return algorithms.ToArray (); - //} - - static DigestAlgorithm[] ParseDigestAlgorithms (string value) - { - var names = value.Split (Whitespace, StringSplitOptions.RemoveEmptyEntries); - var algorithms = new List (); - var seen = new HashSet (); - - for (int i = 0; i < names.Length; i++) { - var name = names[i].ToUpperInvariant (); - DigestAlgorithm algorithm; - - if (DigestAlgorithms.TryGetValue (name, out algorithm) && seen.Add (algorithm)) - algorithms.Add (algorithm); - } - - if (!seen.Contains (DigestAlgorithm.Sha1)) - algorithms.Add (DigestAlgorithm.Sha1); - - return algorithms.ToArray (); - } - - void UpdatePersonalCipherPreferences (string value) - { - EncryptionAlgorithmRank = ParseEncryptionAlgorithms (value); - } - - void UpdatePersonalDigestPreferences (string value) - { - DigestAlgorithmRank = ParseDigestAlgorithms (value); - } - - void LoadConfiguration () - { - if (!File.Exists (Configuration)) - return; - - using (var reader = File.OpenText (Configuration)) { - string line; - - while ((line = reader.ReadLine ()) != null) { - int startIndex = 0; - - while (startIndex < line.Length && char.IsWhiteSpace (line[startIndex])) - startIndex++; - - if (startIndex == line.Length || line[startIndex] == '#') - continue; - - int endIndex = startIndex; - while (endIndex < line.Length && !char.IsWhiteSpace (line[endIndex])) - endIndex++; - - var option = line.Substring (startIndex, endIndex - startIndex); - string value; - - if (endIndex < line.Length) - value = line.Substring (endIndex + 1).Trim (); - else - value = null; - - switch (option) { - case "keyserver": - UpdateKeyServer (value); - break; - case "keyserver-options": - UpdateKeyServerOptions (value); - break; - case "personal-cipher-preferences": - UpdatePersonalCipherPreferences (value); - break; - case "personal-digest-preferences": - UpdatePersonalDigestPreferences (value); - break; - //case "personal-compress-preferences": - // break; - } - } - } - } - } -} diff --git a/src/MimeKit/Cryptography/IDigitalCertificate.cs b/src/MimeKit/Cryptography/IDigitalCertificate.cs deleted file mode 100644 index 45e6fac..0000000 --- a/src/MimeKit/Cryptography/IDigitalCertificate.cs +++ /dev/null @@ -1,92 +0,0 @@ -// -// IDigitalSigner.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Cryptography { - /// - /// An interface for a digital certificate. - /// - /// - /// An interface for a digital certificate. - /// - public interface IDigitalCertificate - { - /// - /// Gets the public key algorithm supported by the certificate. - /// - /// - /// Gets the public key algorithm supported by the certificate. - /// - /// The public key algorithm. - PublicKeyAlgorithm PublicKeyAlgorithm { get; } - - /// - /// Gets the date that the certificate was created. - /// - /// - /// Gets the date that the certificate was created. - /// - /// The creation date. - DateTime CreationDate { get; } - - /// - /// Gets the expiration date of the certificate. - /// - /// - /// Gets the expiration date of the certificate. - /// - /// The expiration date. - DateTime ExpirationDate { get; } - - /// - /// Gets the fingerprint of the certificate. - /// - /// - /// Gets the fingerprint of the certificate. - /// - /// The fingerprint. - string Fingerprint { get; } - - /// - /// Gets the email address of the owner of the certificate. - /// - /// - /// Gets the email address of the owner of the certificate. - /// - /// The email address. - string Email { get; } - - /// - /// Gets the name of the owner of the certificate. - /// - /// - /// Gets the name of the owner of the certificate. - /// - /// The name of the owner. - string Name { get; } - } -} diff --git a/src/MimeKit/Cryptography/IDigitalSignature.cs b/src/MimeKit/Cryptography/IDigitalSignature.cs deleted file mode 100644 index aa26eb0..0000000 --- a/src/MimeKit/Cryptography/IDigitalSignature.cs +++ /dev/null @@ -1,99 +0,0 @@ -// -// DigitalSignature.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Cryptography { - /// - /// An interface for a digital signature. - /// - /// - /// An interface for a digital signature. - /// - public interface IDigitalSignature - { - /// - /// Gets certificate used by the signer. - /// - /// - /// Gets certificate used by the signer. - /// - /// The signer's certificate. - IDigitalCertificate SignerCertificate { get; } - - /// - /// Gets the public key algorithm used for the signature. - /// - /// - /// Gets the public key algorithm used for the signature. - /// - /// The public key algorithm. - PublicKeyAlgorithm PublicKeyAlgorithm { get; } - - /// - /// Gets the digest algorithm used for the signature. - /// - /// - /// Gets the digest algorithm used for the signature. - /// - /// The digest algorithm. - DigestAlgorithm DigestAlgorithm { get; } - - /// - /// Gets the creation date of the digital signature. - /// - /// - /// Gets the creation date of the digital signature. - /// - /// The creation date. - DateTime CreationDate { get; } - - /// - /// Verifies the digital signature. - /// - /// - /// Verifies the digital signature. - /// - /// true if the signature is valid; otherwise, false. - /// - /// An error verifying the signature has occurred. - /// - bool Verify (); - - /// - /// Verifies the digital signature. - /// - /// - /// Verifies the digital signature. - /// - /// true if only the signature itself should be verified; otherwise, both the signature and the certificate chain are validated. - /// true if the signature is valid; otherwise, false. - /// - /// An error verifying the signature has occurred. - /// - bool Verify (bool verifySignatureOnly); - } -} diff --git a/src/MimeKit/Cryptography/IDkimPublicKeyLocator.cs b/src/MimeKit/Cryptography/IDkimPublicKeyLocator.cs deleted file mode 100644 index c729dbf..0000000 --- a/src/MimeKit/Cryptography/IDkimPublicKeyLocator.cs +++ /dev/null @@ -1,96 +0,0 @@ -// -// IDkimPublicKeyLocator.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Threading; -using System.Threading.Tasks; - -using Org.BouncyCastle.Crypto; - -namespace MimeKit.Cryptography { - /// - /// An interface for a service which locates and retrieves DKIM public keys (probably via DNS). - /// - /// - /// An interface for a service which locates and retrieves DKIM public keys (probably via DNS). - /// Since MimeKit itself does not implement DNS, it is up to the client to implement public key lookups - /// via DNS. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public interface IDkimPublicKeyLocator - { - /// - /// Locate and retrieve the public key for the given domain and selector. - /// - /// - /// Locates and retrieves the public key for the given domain and selector. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// The public key. - /// A colon-separated list of query methods used to retrieve the public key. The default is "dns/txt". - /// The domain. - /// The selector. - /// The cancellation token. - AsymmetricKeyParameter LocatePublicKey (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously locate and retrieve the public key for the given domain and selector. - /// - /// - /// Locates and retrieves the public key for the given domain and selector. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// The public key. - /// A colon-separated list of query methods used to retrieve the public key. The default is "dns/txt". - /// The domain. - /// The selector. - /// The cancellation token. - Task LocatePublicKeyAsync (string methods, string domain, string selector, CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MimeKit/Cryptography/IX509CertificateDatabase.cs b/src/MimeKit/Cryptography/IX509CertificateDatabase.cs deleted file mode 100644 index 9be106a..0000000 --- a/src/MimeKit/Cryptography/IX509CertificateDatabase.cs +++ /dev/null @@ -1,196 +0,0 @@ -// -// IX509CertificateDatabase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.X509.Store; - -namespace MimeKit.Cryptography { - /// - /// An interface for an X.509 Certificate database. - /// - /// - /// An X.509 certificate database is used for storing certificates, metdata related to the certificates - /// (such as encryption algorithms supported by the associated client), certificate revocation lists (CRLs), - /// and private keys. - /// - public interface IX509CertificateDatabase : IX509Store, IDisposable - { - /// - /// Find the specified certificate. - /// - /// - /// Searches the database for the specified certificate, returning the matching - /// record with the desired fields populated. - /// - /// The matching record if found; otherwise null. - /// The certificate. - /// The desired fields. - X509CertificateRecord Find (X509Certificate certificate, X509CertificateRecordFields fields); - - /// - /// Finds the certificates matching the specified selector. - /// - /// - /// Searches the database for certificates matching the selector, returning all - /// matching certificates. - /// - /// The matching certificates. - /// The match selector or null to return all certificates. - IEnumerable FindCertificates (IX509Selector selector); - - /// - /// Finds the private keys matching the specified selector. - /// - /// - /// Searches the database for certificate records matching the selector, returning the - /// private keys for each matching record. - /// - /// The matching certificates. - /// The match selector or null to return all private keys. - IEnumerable FindPrivateKeys (IX509Selector selector); - - /// - /// Finds the certificate records for the specified mailbox. - /// - /// - /// Searches the database for certificates matching the specified mailbox that are valid - /// for the date and time specified, returning all matching records populated with the - /// desired fields. - /// - /// The matching certificate records populated with the desired fields. - /// The mailbox. - /// The date and time. - /// true if a private key is required. - /// The desired fields. - IEnumerable Find (MailboxAddress mailbox, DateTime now, bool requirePrivateKey, X509CertificateRecordFields fields); - - /// - /// Finds the certificate records matching the specified selector. - /// - /// - /// Searches the database for certificate records matching the selector, returning all - /// of the matching records populated with the desired fields. - /// - /// The matching certificate records populated with the desired fields. - /// The match selector or null to match all certificates. - /// true if only trusted anchor certificates should be returned. - /// The desired fields. - IEnumerable Find (IX509Selector selector, bool trustedAnchorsOnly, X509CertificateRecordFields fields); - - /// - /// Add the specified certificate record. - /// - /// - /// Adds the specified certificate record to the database. - /// - /// The certificate record. - void Add (X509CertificateRecord record); - - /// - /// Remove the specified certificate record. - /// - /// - /// Removes the specified certificate record from the database. - /// - /// The certificate record. - void Remove (X509CertificateRecord record); - - /// - /// Update the specified certificate record. - /// - /// - /// Updates the specified fields of the record in the database. - /// - /// The certificate record. - /// The fields to update. - void Update (X509CertificateRecord record, X509CertificateRecordFields fields); - - /// - /// Finds the CRL records for the specified issuer. - /// - /// - /// Searches the database for CRL records matching the specified issuer, returning - /// all matching records populated with the desired fields. - /// - /// The matching CRL records populated with the desired fields. - /// The issuer. - /// The desired fields. - IEnumerable Find (X509Name issuer, X509CrlRecordFields fields); - - /// - /// Finds the specified certificate revocation list. - /// - /// - /// Searches the database for the specified CRL, returning the matching record with - /// the desired fields populated. - /// - /// The matching record if found; otherwise null. - /// The certificate revocation list. - /// The desired fields. - X509CrlRecord Find (X509Crl crl, X509CrlRecordFields fields); - - /// - /// Add the specified CRL record. - /// - /// - /// Adds the specified CRL record to the database. - /// - /// The CRL record. - void Add (X509CrlRecord record); - - /// - /// Remove the specified CRL record. - /// - /// - /// Removes the specified CRL record from the database. - /// - /// The CRL record. - void Remove (X509CrlRecord record); - - /// - /// Update the specified CRL record. - /// - /// - /// Updates the specified fields of the record in the database. - /// - /// The CRL record. - void Update (X509CrlRecord record); - - /// - /// Gets a certificate revocation list store. - /// - /// - /// Gets a certificate revocation list store. - /// - /// A certificate recovation list store. - IX509Store GetCrlStore (); - } -} diff --git a/src/MimeKit/Cryptography/LdapUri.cs b/src/MimeKit/Cryptography/LdapUri.cs deleted file mode 100644 index 0533814..0000000 --- a/src/MimeKit/Cryptography/LdapUri.cs +++ /dev/null @@ -1,161 +0,0 @@ -// -// LdapUri.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2019 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.DirectoryServices.Protocols; - -namespace MimeKit.Cryptography -{ - class LdapUri - { - static readonly char[] EndOfHostPort = { ':', '/' }; - static readonly char[] Comma = { ',' }; - - const string DefaultFilter = "(objectClass=*)"; - - public string Scheme { get; private set; } - public string Host { get; private set; } - public int Port { get; private set; } - public string DistinguishedName { get; private set; } - public string[] Attributes { get; private set; } - public SearchScope Scope { get; private set; } - public string Filter { get; private set; } - public string[] Extensions { get; private set; } - - public LdapUri (string scheme) - { - Scheme = scheme; - Host = string.Empty; - DistinguishedName = string.Empty; - Attributes = new string[] { "*" }; - Scope = SearchScope.Base; - Filter = DefaultFilter; - } - - public static bool TryParse (string location, out LdapUri uri) - { - // https://www.ietf.org/rfc/rfc2255.txt - int startIndex, index; - string value; - - uri = null; - - // parse the scheme - if ((index = location.IndexOf (':')) == -1 || index + 2 >= location.Length || location[index + 1] != '/' || location[index + 2] != '/') - return false; - - uri = new LdapUri (location.Substring (0, index)); - - if ((startIndex = index + 3) >= location.Length) - return true; - - // parse the hostname - if ((index = location.IndexOfAny (EndOfHostPort, startIndex)) == -1) - index = location.Length; - - uri.Host = location.Substring (startIndex, index - startIndex); - - if (index < location.Length && location[index] == ':') { - if ((startIndex = index + 1) >= location.Length) - return false; - - // parse the port - if ((index = location.IndexOf ('/', startIndex)) == -1) - index = location.Length; - - value = location.Substring (startIndex, index - startIndex); - if (!ushort.TryParse (value, out ushort port)) - return false; - - uri.Port = port; - } - - if ((startIndex = index + 1) >= location.Length) - return true; - - // parse the distinguished-name - if ((index = location.IndexOf ('?')) == -1) - index = location.Length; - - value = location.Substring (startIndex, index - startIndex); - uri.DistinguishedName = Uri.UnescapeDataString (value); - - if ((startIndex = index + 1) >= location.Length) - return true; - - // parse the attributes - if ((index = location.IndexOf ('?', startIndex)) == -1) - index = location.Length; - - if (index > startIndex) { - value = location.Substring (startIndex, index - startIndex); - uri.Attributes = value.Split (Comma, StringSplitOptions.RemoveEmptyEntries).Select (attr => Uri.UnescapeDataString (attr)).ToArray (); - } - - if ((startIndex = index + 1) >= location.Length) - return true; - - // parse the scope - if ((index = location.IndexOf ('?', startIndex)) == -1) - index = location.Length; - - value = location.Substring (startIndex, index - startIndex); - switch (value.ToLowerInvariant ()) { - case "base": uri.Scope = SearchScope.Base; break; - case "one": uri.Scope = SearchScope.OneLevel; break; - case "sub": uri.Scope = SearchScope.Subtree; break; - default: - // Note: Assuming that Example #7 in rfc5522 is correctly formed, then - // we need to backtrack and parse this as the filter instead. - index = startIndex - 1; - break; - } - - if ((startIndex = index + 1) >= location.Length) - return true; - - // parse the filter - if ((index = location.IndexOf ('?', startIndex)) == -1) - index = location.Length; - - if (index > startIndex) { - value = location.Substring (startIndex, index - startIndex); - uri.Filter = Uri.UnescapeDataString (value); - } - - if ((startIndex = index + 1) >= location.Length) - return true; - - index = location.Length; - - value = location.Substring (startIndex, index - startIndex); - uri.Extensions = value.Split (Comma, StringSplitOptions.RemoveEmptyEntries).Select (extn => Uri.UnescapeDataString (extn)).ToArray (); - - return true; - } - } -} diff --git a/src/MimeKit/Cryptography/MD5.cs b/src/MimeKit/Cryptography/MD5.cs deleted file mode 100644 index 10719be..0000000 --- a/src/MimeKit/Cryptography/MD5.cs +++ /dev/null @@ -1,749 +0,0 @@ -// -// System.Security.Cryptography.MD5CryptoServiceProvider.cs -// -// Authors: -// Matthew S. Ford (Matthew.S.Ford@Rose-Hulman.Edu) -// Sebastien Pouliot (sebastien@ximian.com) -// -// Copyright 2001 by Matthew S. Ford. -// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.IO; - -namespace MimeKit.Cryptography { - /// - /// The MD5 hash algorithm. - /// - /// - /// This class is only here for for portability reasons and should - /// not really be considered part of the MimeKit API. - /// - public sealed class MD5 : IDisposable - { - const int BLOCK_SIZE_BYTES = 64; - - static readonly uint[] K = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, - 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 - }; - - byte[] hashValue; - byte[] queuedData; // Used to store data when passed less than a block worth. - int queuedCount; // Counts how much data we have stored that still needs processed. - uint[] _H, buff; - bool disposed; - ulong count; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new instance of an MD5 hash algorithm context. - /// - MD5 () - { - queuedData = new byte [BLOCK_SIZE_BYTES]; - buff = new uint[16]; - _H = new uint[4]; - - Initialize (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new instance of an MD5 hash algorithm context. - /// - public static MD5 Create () - { - return new MD5 (); - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~MD5 () - { - Dispose (false); - } - - /// - /// Gets the value of the computed hash code. - /// - /// - /// Gets the value of the computed hash code. - /// - /// The computed hash code. - /// - /// No hash value has been computed. - /// - public byte[] Hash { - get { - if (hashValue == null) - throw new InvalidOperationException ("No hash value computed."); - - return hashValue; - } - } - - void HashCore (byte[] block, int offset, int size) - { - int i; - - if (queuedCount != 0) { - if (size < (BLOCK_SIZE_BYTES - queuedCount)) { - Buffer.BlockCopy (block, offset, queuedData, queuedCount, size); - queuedCount += size; - return; - } - - i = (BLOCK_SIZE_BYTES - queuedCount); - Buffer.BlockCopy (block, offset, queuedData, queuedCount, i); - ProcessBlock (queuedData, 0); - queuedCount = 0; - offset += i; - size -= i; - } - - for (i = 0; i < size - size % BLOCK_SIZE_BYTES; i += BLOCK_SIZE_BYTES) - ProcessBlock (block, offset + i); - - if (size % BLOCK_SIZE_BYTES != 0) { - Buffer.BlockCopy (block, size - size % BLOCK_SIZE_BYTES + offset, queuedData, 0, size % BLOCK_SIZE_BYTES); - queuedCount = size % BLOCK_SIZE_BYTES; - } - } - - byte[] HashFinal () - { - byte[] hash = new byte[16]; - int i, j; - - ProcessFinalBlock (queuedData, 0, queuedCount); - - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - hash[i * 4 + j] = (byte)(_H[i] >> j * 8); - } - } - - return hash; - } - - /// - /// Initializes (or re-initializes) the MD5 hash algorithm context. - /// - /// - /// Initializes (or re-initializes) the MD5 hash algorithm context. - /// - public void Initialize () - { - queuedCount = 0; - count = 0; - - _H[0] = 0x67452301; - _H[1] = 0xefcdab89; - _H[2] = 0x98badcfe; - _H[3] = 0x10325476; - } - - void ProcessBlock (byte[] block, int offset) - { - uint a, b, c, d; - int i; - - count += BLOCK_SIZE_BYTES; - - for (i = 0; i < 16; i++) { - buff[i] = (uint)(block[offset + 4 * i]) - | (((uint)(block[offset + 4 * i + 1])) << 8) - | (((uint)(block[offset + 4 * i + 2])) << 16) - | (((uint)(block[offset + 4 * i + 3])) << 24); - } - - a = _H[0]; - b = _H[1]; - c = _H[2]; - d = _H[3]; - - // This function was unrolled because it seems to be doubling our performance with current compiler/VM. - // Possibly roll up if this changes. - - // ---- Round 1 -------- - - a += (((c ^ d) & b) ^ d) + (uint) K[0] + buff[0]; - a = (a << 7) | (a >> 25); - a += b; - - d += (((b ^ c) & a) ^ c) + (uint) K[1] + buff[1]; - d = (d << 12) | (d >> 20); - d += a; - - c += (((a ^ b) & d) ^ b) + (uint) K[2] + buff[2]; - c = (c << 17) | (c >> 15); - c += d; - - b += (((d ^ a) & c) ^ a) + (uint) K[3] + buff[3]; - b = (b << 22) | (b >> 10); - b += c; - - a += (((c ^ d) & b) ^ d) + (uint) K[4] + buff[4]; - a = (a << 7) | (a >> 25); - a += b; - - d += (((b ^ c) & a) ^ c) + (uint) K[5] + buff[5]; - d = (d << 12) | (d >> 20); - d += a; - - c += (((a ^ b) & d) ^ b) + (uint) K[6] + buff[6]; - c = (c << 17) | (c >> 15); - c += d; - - b += (((d ^ a) & c) ^ a) + (uint) K[7] + buff[7]; - b = (b << 22) | (b >> 10); - b += c; - - a += (((c ^ d) & b) ^ d) + (uint) K[8] + buff[8]; - a = (a << 7) | (a >> 25); - a += b; - - d += (((b ^ c) & a) ^ c) + (uint) K[9] + buff[9]; - d = (d << 12) | (d >> 20); - d += a; - - c += (((a ^ b) & d) ^ b) + (uint) K[10] + buff[10]; - c = (c << 17) | (c >> 15); - c += d; - - b += (((d ^ a) & c) ^ a) + (uint) K[11] + buff[11]; - b = (b << 22) | (b >> 10); - b += c; - - a += (((c ^ d) & b) ^ d) + (uint) K[12] + buff[12]; - a = (a << 7) | (a >> 25); - a += b; - - d += (((b ^ c) & a) ^ c) + (uint) K[13] + buff[13]; - d = (d << 12) | (d >> 20); - d += a; - - c += (((a ^ b) & d) ^ b) + (uint) K[14] + buff[14]; - c = (c << 17) | (c >> 15); - c += d; - - b += (((d ^ a) & c) ^ a) + (uint) K[15] + buff[15]; - b = (b << 22) | (b >> 10); - b += c; - - - // ---- Round 2 -------- - - a += (((b ^ c) & d) ^ c) + (uint) K[16] + buff[1]; - a = (a << 5) | (a >> 27); - a += b; - - d += (((a ^ b) & c) ^ b) + (uint) K[17] + buff[6]; - d = (d << 9) | (d >> 23); - d += a; - - c += (((d ^ a) & b) ^ a) + (uint) K[18] + buff[11]; - c = (c << 14) | (c >> 18); - c += d; - - b += (((c ^ d) & a) ^ d) + (uint) K[19] + buff[0]; - b = (b << 20) | (b >> 12); - b += c; - - a += (((b ^ c) & d) ^ c) + (uint) K[20] + buff[5]; - a = (a << 5) | (a >> 27); - a += b; - - d += (((a ^ b) & c) ^ b) + (uint) K[21] + buff[10]; - d = (d << 9) | (d >> 23); - d += a; - - c += (((d ^ a) & b) ^ a) + (uint) K[22] + buff[15]; - c = (c << 14) | (c >> 18); - c += d; - - b += (((c ^ d) & a) ^ d) + (uint) K[23] + buff[4]; - b = (b << 20) | (b >> 12); - b += c; - - a += (((b ^ c) & d) ^ c) + (uint) K[24] + buff[9]; - a = (a << 5) | (a >> 27); - a += b; - - d += (((a ^ b) & c) ^ b) + (uint) K[25] + buff[14]; - d = (d << 9) | (d >> 23); - d += a; - - c += (((d ^ a) & b) ^ a) + (uint) K[26] + buff[3]; - c = (c << 14) | (c >> 18); - c += d; - - b += (((c ^ d) & a) ^ d) + (uint) K[27] + buff[8]; - b = (b << 20) | (b >> 12); - b += c; - - a += (((b ^ c) & d) ^ c) + (uint) K[28] + buff[13]; - a = (a << 5) | (a >> 27); - a += b; - - d += (((a ^ b) & c) ^ b) + (uint) K[29] + buff[2]; - d = (d << 9) | (d >> 23); - d += a; - - c += (((d ^ a) & b) ^ a) + (uint) K[30] + buff[7]; - c = (c << 14) | (c >> 18); - c += d; - - b += (((c ^ d) & a) ^ d) + (uint) K[31] + buff[12]; - b = (b << 20) | (b >> 12); - b += c; - - - // ---- Round 3 -------- - - a += (b ^ c ^ d) + (uint) K[32] + buff[5]; - a = (a << 4) | (a >> 28); - a += b; - - d += (a ^ b ^ c) + (uint) K[33] + buff[8]; - d = (d << 11) | (d >> 21); - d += a; - - c += (d ^ a ^ b) + (uint) K[34] + buff[11]; - c = (c << 16) | (c >> 16); - c += d; - - b += (c ^ d ^ a) + (uint) K[35] + buff[14]; - b = (b << 23) | (b >> 9); - b += c; - - a += (b ^ c ^ d) + (uint) K[36] + buff[1]; - a = (a << 4) | (a >> 28); - a += b; - - d += (a ^ b ^ c) + (uint) K[37] + buff[4]; - d = (d << 11) | (d >> 21); - d += a; - - c += (d ^ a ^ b) + (uint) K[38] + buff[7]; - c = (c << 16) | (c >> 16); - c += d; - - b += (c ^ d ^ a) + (uint) K[39] + buff[10]; - b = (b << 23) | (b >> 9); - b += c; - - a += (b ^ c ^ d) + (uint) K[40] + buff[13]; - a = (a << 4) | (a >> 28); - a += b; - - d += (a ^ b ^ c) + (uint) K[41] + buff[0]; - d = (d << 11) | (d >> 21); - d += a; - - c += (d ^ a ^ b) + (uint) K[42] + buff[3]; - c = (c << 16) | (c >> 16); - c += d; - - b += (c ^ d ^ a) + (uint) K[43] + buff[6]; - b = (b << 23) | (b >> 9); - b += c; - - a += (b ^ c ^ d) + (uint) K[44] + buff[9]; - a = (a << 4) | (a >> 28); - a += b; - - d += (a ^ b ^ c) + (uint) K[45] + buff[12]; - d = (d << 11) | (d >> 21); - d += a; - - c += (d ^ a ^ b) + (uint) K[46] + buff[15]; - c = (c << 16) | (c >> 16); - c += d; - - b += (c ^ d ^ a) + (uint) K[47] + buff[2]; - b = (b << 23) | (b >> 9); - b += c; - - - // ---- Round 4 -------- - - a += (((~d) | b) ^ c) + (uint) K[48] + buff[0]; - a = (a << 6) | (a >> 26); - a += b; - - d += (((~c) | a) ^ b) + (uint) K[49] + buff[7]; - d = (d << 10) | (d >> 22); - d += a; - - c += (((~b) | d) ^ a) + (uint) K[50] + buff[14]; - c = (c << 15) | (c >> 17); - c += d; - - b += (((~a) | c) ^ d) + (uint) K[51] + buff[5]; - b = (b << 21) | (b >> 11); - b += c; - - a += (((~d) | b) ^ c) + (uint) K[52] + buff[12]; - a = (a << 6) | (a >> 26); - a += b; - - d += (((~c) | a) ^ b) + (uint) K[53] + buff[3]; - d = (d << 10) | (d >> 22); - d += a; - - c += (((~b) | d) ^ a) + (uint) K[54] + buff[10]; - c = (c << 15) | (c >> 17); - c += d; - - b += (((~a) | c) ^ d) + (uint) K[55] + buff[1]; - b = (b << 21) | (b >> 11); - b += c; - - a += (((~d) | b) ^ c) + (uint) K[56] + buff[8]; - a = (a << 6) | (a >> 26); - a += b; - - d += (((~c) | a) ^ b) + (uint) K[57] + buff[15]; - d = (d << 10) | (d >> 22); - d += a; - - c += (((~b) | d) ^ a) + (uint) K[58] + buff[6]; - c = (c << 15) | (c >> 17); - c += d; - - b += (((~a) | c) ^ d) + (uint) K[59] + buff[13]; - b = (b << 21) | (b >> 11); - b += c; - - a += (((~d) | b) ^ c) + (uint) K[60] + buff[4]; - a = (a << 6) | (a >> 26); - a += b; - - d += (((~c) | a) ^ b) + (uint) K[61] + buff[11]; - d = (d << 10) | (d >> 22); - d += a; - - c += (((~b) | d) ^ a) + (uint) K[62] + buff[2]; - c = (c << 15) | (c >> 17); - c += d; - - b += (((~a) | c) ^ d) + (uint) K[63] + buff[9]; - b = (b << 21) | (b >> 11); - b += c; - - _H[0] += a; - _H[1] += b; - _H[2] += c; - _H[3] += d; - } - - void ProcessFinalBlock (byte[] inbuf, int startIndex, int length) - { - ulong total = count + (ulong) length; - int padding = (int)(56 - total % BLOCK_SIZE_BYTES); - - if (padding < 1) - padding += BLOCK_SIZE_BYTES; - - var block = new byte [length + padding + 8]; - - for (int i = 0; i < length; i++) - block[i] = inbuf[startIndex + i]; - - block[length] = 0x80; - for (int i = length + 1; i < length + padding; i++) - block[i] = 0x00; - - // I deal in bytes. The algorithm deals in bits. - ulong size = total << 3; - AddLength (size, block, length+padding); - ProcessBlock (block, 0); - - if (length + padding + 8 == 128) - ProcessBlock (block, 64); - } - - void AddLength (ulong length, byte[] buffer, int index) - { - buffer[index++] = (byte) length; - buffer[index++] = (byte) (length >> 8); - buffer[index++] = (byte) (length >> 16); - buffer[index++] = (byte) (length >> 24); - buffer[index++] = (byte) (length >> 32); - buffer[index++] = (byte) (length >> 40); - buffer[index++] = (byte) (length >> 48); - buffer[index] = (byte) (length >> 56); - } - - /// - /// Computes the MD5 hash code for the specified subrange of the buffer. - /// - /// - /// Computes the MD5 hash code for the specified subrange of the buffer. - /// - /// The computed hash code. - /// The buffer. - /// The starting offset. - /// The number of bytes to hash. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// The MD5 context has been disposed. - /// - public byte[] ComputeHash (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException ("buffer"); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException ("offset"); - - if (count < 0 || offset > buffer.Length - count) - throw new ArgumentOutOfRangeException ("count"); - - if (disposed) - throw new ObjectDisposedException ("HashAlgorithm"); - - HashCore (buffer, offset, count); - hashValue = HashFinal (); - Initialize (); - - return hashValue; - } - - /// - /// Computes the MD5 hash code for the buffer. - /// - /// - /// Computes the MD5 hash code for the buffer. - /// - /// The computed hash code. - /// The buffer. - /// - /// is null. - /// - /// - /// The MD5 context has been disposed. - /// - public byte[] ComputeHash (byte[] buffer) - { - if (buffer == null) - throw new ArgumentNullException ("buffer"); - - return ComputeHash (buffer, 0, buffer.Length); - } - - /// - /// Computes the MD5 hash code for the stream. - /// - /// - /// Computes the MD5 hash code for the stream. - /// - /// The computed hash code. - /// The input stream. - /// - /// is null. - /// - /// - /// The MD5 context has been disposed. - /// - public byte[] ComputeHash (Stream inputStream) - { - if (inputStream == null) - throw new ArgumentNullException ("inputStream"); - - // don't read stream unless object is ready to use - if (disposed) - throw new ObjectDisposedException ("HashAlgorithm"); - - var buffer = new byte [4096]; - int nread; - - do { - if ((nread = inputStream.Read (buffer, 0, buffer.Length)) > 0) - HashCore (buffer, 0, nread); - } while (nread > 0); - - hashValue = HashFinal (); - Initialize (); - - return hashValue; - } - - /// - /// Computes a partial MD5 hash value for the specified region of the - /// input buffer and copies the input into the output buffer. - /// - /// - /// Computes a partial MD5 hash value for the specified region of the - /// input buffer and copies the input into the output buffer. - /// Use to complete the computation - /// of the MD5 hash code. - /// - /// The number of bytes copied into the output buffer. - /// The input buffer. - /// The input buffer offset. - /// The input count. - /// The output buffer. - /// The output buffer offset. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the . - /// -or- - /// is outside the bounds of the - /// . - /// -or- - /// is not large enough to hold the range of input - /// starting at . - /// - public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer == null) - throw new ArgumentNullException ("inputBuffer"); - - if (inputOffset < 0 || inputOffset > inputBuffer.Length) - throw new ArgumentOutOfRangeException ("inputOffset"); - - if (inputCount < 0 || inputOffset > inputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException ("inputCount"); - - if (outputBuffer != null) { - if (outputOffset < 0 || outputOffset > outputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException ("outputOffset"); - } - - HashCore (inputBuffer, inputOffset, inputCount); - - if (outputBuffer != null) - Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); - - return inputCount; - } - - /// - /// Completes the MD5 hash compuation given the final block of input. - /// - /// - /// Completes the MD5 hash compuation given the final block of input. - /// - /// A new buffer containing the specified range of input. - /// The input buffer. - /// The input buffer offset. - /// The input count. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the . - /// - public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) - { - if (inputBuffer == null) - throw new ArgumentNullException ("inputBuffer"); - - if (inputOffset < 0 || inputOffset > inputBuffer.Length) - throw new ArgumentOutOfRangeException ("inputOffset"); - - if (inputCount < 0 || inputOffset > inputBuffer.Length - inputCount) - throw new ArgumentOutOfRangeException ("inputCount"); - - var outputBuffer = new byte [inputCount]; - - // note: other exceptions are handled by Buffer.BlockCopy - Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, 0, inputCount); - - HashCore (inputBuffer, inputOffset, inputCount); - hashValue = HashFinal (); - Initialize (); - - return outputBuffer; - } - - void Dispose (bool disposing) - { - if (queuedData != null) { - Array.Clear (queuedData, 0, queuedData.Length); - queuedData = null; - } - - if (_H != null) { - Array.Clear (_H, 0, _H.Length); - _H = null; - } - - if (buff != null) { - Array.Clear (buff, 0, buff.Length); - buff = null; - } - } - - /// - /// Releases all resource used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After calling - /// , you must release all references to the so the - /// garbage collector can reclaim the memory that the was occupying. - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - disposed = true; - } - } -} diff --git a/src/MimeKit/Cryptography/MacSecureMimeContext.cs b/src/MimeKit/Cryptography/MacSecureMimeContext.cs deleted file mode 100644 index 5788738..0000000 --- a/src/MimeKit/Cryptography/MacSecureMimeContext.cs +++ /dev/null @@ -1,267 +0,0 @@ -// -// MacSecureMimeContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2014 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections.Generic; - -using Org.BouncyCastle.Pkix; -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.X509.Store; - -using MimeKit.MacInterop; - -namespace MimeKit.Cryptography { - public class MacSecureMimeContext : SecureMimeContext - { - SecKeychain keychain; - - public MacSecureMimeContext (string path, string password) - { - if (path == null) - throw new ArgumentNullException ("path"); - - if (password == null) - throw new ArgumentNullException ("password"); - - keychain = SecKeychain.Create (path, password); - } - - public MacSecureMimeContext () - { - keychain = SecKeychain.Default; - } - - #region implemented abstract members of SecureMimeContext - - /// - /// Gets the X.509 certificate based on the selector. - /// - /// The certificate on success; otherwise null. - /// The search criteria for the certificate. - protected override X509Certificate GetCertificate (IX509Selector selector) - { - foreach (var certificate in keychain.GetCertificates ((CssmKeyUse) 0)) { - if (selector.Match (certificate)) - return certificate; - } - - return null; - } - - /// - /// Gets the private key based on the provided selector. - /// - /// The private key on success; otherwise null. - /// The search criteria for the private key. - protected override AsymmetricKeyParameter GetPrivateKey (IX509Selector selector) - { - foreach (var signer in keychain.GetAllCmsSigners ()) { - if (selector.Match (signer.Certificate)) - return signer.PrivateKey; - } - - return null; - } - - /// - /// Gets the trust anchors. - /// - /// The trust anchors. - protected override Org.BouncyCastle.Utilities.Collections.HashSet GetTrustedAnchors () - { - var anchors = new Org.BouncyCastle.Utilities.Collections.HashSet (); - - // FIXME: how do we get the trusted root certs? - - return anchors; - } - - /// - /// Gets the intermediate certificates. - /// - /// The intermediate certificates. - protected override IX509Store GetIntermediateCertificates () - { - var store = new X509CertificateStore (); - - foreach (var certificate in keychain.GetCertificates ((CssmKeyUse) 0)) { - store.Add (certificate); - } - - return store; - } - - /// - /// Gets the certificate revocation lists. - /// - /// The certificate revocation lists. - protected override IX509Store GetCertificateRevocationLists () - { - var crls = new List (); - - return X509StoreFactory.Create ("Crl/Collection", new X509CollectionStoreParameters (crls)); - } - - /// - /// Gets the for the specified mailbox. - /// - /// A . - /// The mailbox. - /// - /// A certificate for the specified could not be found. - /// - protected override CmsRecipient GetCmsRecipient (MailboxAddress mailbox) - { - foreach (var certificate in keychain.GetCertificates (CssmKeyUse.Encrypt)) { - if (certificate.GetSubjectEmailAddress () == mailbox.Address) - return new CmsRecipient (certificate); - } - - throw new CertificateNotFoundException (mailbox, "A valid certificate could not be found."); - } - - /// - /// Gets the for the specified mailbox. - /// - /// A . - /// The mailbox. - /// The preferred digest algorithm. - /// - /// A certificate for the specified could not be found. - /// - protected override CmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgorithm digestAlgo) - { - foreach (var signer in keychain.GetAllCmsSigners ()) { - if (signer.Certificate.GetSubjectEmailAddress () == mailbox.Address) { - signer.DigestAlgorithm = digestAlgo; - return signer; - } - } - - throw new CertificateNotFoundException (mailbox, "A valid signing certificate could not be found."); - } - - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// - /// The certificate. - /// The encryption algorithm capabilities of the client (in preferred order). - /// The timestamp. - protected override void UpdateSecureMimeCapabilities (X509Certificate certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp) - { - // FIXME: implement this - } - - /// - /// Import the specified certificate. - /// - /// The certificate. - /// - /// is null. - /// - public override void Import (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException ("certificate"); - - keychain.Add (certificate); - } - - /// - /// Import the specified certificate revocation list. - /// - /// The certificate revocation list. - /// - /// is null. - /// - public override void Import (X509Crl crl) - { - if (crl == null) - throw new ArgumentNullException ("crl"); - - // FIXME: implement this - } - - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// The raw certificate and key data. - /// The password to unlock the stream. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// Importing keys is not supported by this cryptography context. - /// - public override void Import (Stream stream, string password) - { - if (stream == null) - throw new ArgumentNullException ("stream"); - - if (password == null) - throw new ArgumentNullException ("password"); - - var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ()); - foreach (string alias in pkcs12.Aliases) { - if (pkcs12.IsKeyEntry (alias)) { - var chain = pkcs12.GetCertificateChain (alias); - var entry = pkcs12.GetKey (alias); - - for (int i = 0; i < chain.Length; i++) - keychain.Add (chain[i].Certificate); - - keychain.Add (entry.Key); - } else if (pkcs12.IsCertificateEntry (alias)) { - var entry = pkcs12.GetCertificate (alias); - keychain.Add (entry.Certificate); - } - } - } - - #endregion - - /// - /// Releases all resources used by the object. - /// - /// If true, this method is being called by - /// ; - /// otherwise it is being called by the finalizer. - protected override void Dispose (bool disposing) - { - if (disposing && keychain != SecKeychain.Default) { - keychain.Dispose (); - keychain = null; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MimeKit/Cryptography/MultipartEncrypted.cs b/src/MimeKit/Cryptography/MultipartEncrypted.cs deleted file mode 100644 index d6a69fc..0000000 --- a/src/MimeKit/Cryptography/MultipartEncrypted.cs +++ /dev/null @@ -1,1207 +0,0 @@ -// -// MultipartEncrypted.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using Org.BouncyCastle.Bcpg.OpenPgp; - -using MimeKit.IO; -using MimeKit.IO.Filters; - -namespace MimeKit.Cryptography { - /// - /// A multipart MIME part with a ContentType of multipart/encrypted containing an encrypted MIME part. - /// - /// - /// This mime-type is common when dealing with PGP/MIME but is not used for S/MIME. - /// - public class MultipartEncrypted : Multipart - { - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MultipartEncrypted (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public MultipartEncrypted () : base ("encrypted") - { - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMultipartEncrypted (this); - } - - /// - /// Create a multipart/encrypted MIME part by signing and encrypting the specified entity. - /// - /// - /// Signs the entity using the supplied signer and digest algorithm and then encrypts to - /// the specified recipients, encapsulating the result in a new multipart/encrypted part. - /// - /// A new instance containing - /// the signed and encrypted version of the specified entity. - /// The OpenPGP cryptography context to use for singing and encrypting. - /// The signer to use to sign the entity. - /// The digest algorithm to use for signing. - /// The encryption algorithm. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// -or- - /// The is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public static MultipartEncrypted SignAndEncrypt (OpenPgpContext ctx, MailboxAddress signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - var encrypted = new MultipartEncrypted (); - encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; - - // add the protocol version part - encrypted.Add (new ApplicationPgpEncrypted ()); - - // add the encrypted entity as the second part - encrypted.Add (ctx.SignAndEncrypt (signer, digestAlgo, cipherAlgo, recipients, memory)); - - return encrypted; - } - } - - /// - /// Create a multipart/encrypted MIME part by signing and encrypting the specified entity. - /// - /// - /// Signs the entity using the supplied signer and digest algorithm and then encrypts to - /// the specified recipients, encapsulating the result in a new multipart/encrypted part. - /// - /// A new instance containing - /// the signed and encrypted version of the specified entity. - /// The OpenPGP cryptography context to use for signing and encrypting. - /// The signer to use to sign the entity. - /// The digest algorithm to use for signing. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The private key for could not be found. - /// - /// - /// A public key for one or more of the could not be found. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public static MultipartEncrypted SignAndEncrypt (OpenPgpContext ctx, MailboxAddress signer, DigestAlgorithm digestAlgo, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - var encrypted = new MultipartEncrypted (); - encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; - - // add the protocol version part - encrypted.Add (new ApplicationPgpEncrypted ()); - - // add the encrypted entity as the second part - encrypted.Add (ctx.SignAndEncrypt (signer, digestAlgo, recipients, memory)); - - return encrypted; - } - } - - /// - /// Create a multipart/encrypted MIME part by signing and encrypting the specified entity. - /// - /// - /// Signs the entity using the supplied signer and digest algorithm and then encrypts to - /// the specified recipients, encapsulating the result in a new multipart/encrypted part. - /// - /// A new instance containing - /// the signed and encrypted version of the specified entity. - /// The signer to use to sign the entity. - /// The digest algorithm to use for signing. - /// The encryption algorithm. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The was out of range. - /// - /// - /// A default has not been registered. - /// -or- - /// The is not supported. - /// -or- - /// The is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public static MultipartEncrypted SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerable recipients, MimeEntity entity) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-encrypted")) - return SignAndEncrypt (ctx, signer, digestAlgo, cipherAlgo, recipients, entity); - } - - /// - /// Create a multipart/encrypted MIME part by signing and encrypting the specified entity. - /// - /// - /// Signs the entity using the supplied signer and digest algorithm and then encrypts to - /// the specified recipients, encapsulating the result in a new multipart/encrypted part. - /// - /// A new instance containing - /// the signed and encrypted version of the specified entity. - /// The signer to use to sign the entity. - /// The digest algorithm to use for signing. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// A default has not been registered. - /// - /// - /// The private key for could not be found. - /// - /// - /// A public key for one or more of the could not be found. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public static MultipartEncrypted SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, IEnumerable recipients, MimeEntity entity) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-encrypted")) - return SignAndEncrypt (ctx, signer, digestAlgo, recipients, entity); - } - - /// - /// Create a multipart/encrypted MIME part by signing and encrypting the specified entity. - /// - /// - /// Signs the entity using the supplied signer and digest algorithm and then encrypts to - /// the specified recipients, encapsulating the result in a new multipart/encrypted part. - /// - /// A new instance containing - /// the signed and encrypted version of the specified entity. - /// The OpenPGP cryptography context to use for singing and encrypting. - /// The signer to use to sign the entity. - /// The digest algorithm to use for signing. - /// The encryption algorithm. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// -or- - /// The is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public static MultipartEncrypted SignAndEncrypt (OpenPgpContext ctx, PgpSecretKey signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - var encrypted = new MultipartEncrypted (); - encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; - - // add the protocol version part - encrypted.Add (new ApplicationPgpEncrypted ()); - - // add the encrypted entity as the second part - encrypted.Add (ctx.SignAndEncrypt (signer, digestAlgo, cipherAlgo, recipients, memory)); - - return encrypted; - } - } - - /// - /// Create a multipart/encrypted MIME part by signing and encrypting the specified entity. - /// - /// - /// Signs the entity using the supplied signer and digest algorithm and then encrypts to - /// the specified recipients, encapsulating the result in a new multipart/encrypted part. - /// - /// A new instance containing - /// the signed and encrypted version of the specified entity. - /// The OpenPGP cryptography context to use for singing and encrypting. - /// The signer to use to sign the entity. - /// The digest algorithm to use for signing. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public static MultipartEncrypted SignAndEncrypt (OpenPgpContext ctx, PgpSecretKey signer, DigestAlgorithm digestAlgo, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - - entity.WriteTo (options, memory); - memory.Position = 0; - - var encrypted = new MultipartEncrypted (); - encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; - - // add the protocol version part - encrypted.Add (new ApplicationPgpEncrypted ()); - - // add the encrypted entity as the second part - encrypted.Add (ctx.SignAndEncrypt (signer, digestAlgo, recipients, memory)); - - return encrypted; - } - } - - /// - /// Create a multipart/encrypted MIME part by signing and encrypting the specified entity. - /// - /// - /// Signs the entity using the supplied signer and digest algorithm and then encrypts to - /// the specified recipients, encapsulating the result in a new multipart/encrypted part. - /// - /// A new instance containing - /// the signed and encrypted version of the specified entity. - /// The signer to use to sign the entity. - /// The digest algorithm to use for signing. - /// The encryption algorithm. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The was out of range. - /// - /// - /// A default has not been registered. - /// -or- - /// The is not supported. - /// -or- - /// The is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public static MultipartEncrypted SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerable recipients, MimeEntity entity) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-encrypted")) - return SignAndEncrypt (ctx, signer, digestAlgo, cipherAlgo, recipients, entity); - } - - /// - /// Create a multipart/encrypted MIME part by signing and encrypting the specified entity. - /// - /// - /// Signs the entity using the supplied signer and digest algorithm and then encrypts to - /// the specified recipients, encapsulating the result in a new multipart/encrypted part. - /// - /// A new instance containing - /// the signed and encrypted version of the specified entity. - /// The signer to use to sign the entity. - /// The digest algorithm to use for signing. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The was out of range. - /// - /// - /// A default has not been registered. - /// -or- - /// The is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public static MultipartEncrypted SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, IEnumerable recipients, MimeEntity entity) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-encrypted")) - return SignAndEncrypt (ctx, signer, digestAlgo, recipients, entity); - } - - /// - /// Create a multipart/encrypted MIME part by encrypting the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients, encapsulating the result in a - /// new multipart/encrypted part. - /// - /// A new instance containing - /// the encrypted version of the specified entity. - /// The OpenPGP cryptography context to use for encrypting. - /// The encryption algorithm. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// - /// - /// THe specified encryption algorithm is not supported. - /// - public static MultipartEncrypted Encrypt (OpenPgpContext ctx, EncryptionAlgorithm algorithm, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - using (var filtered = new FilteredStream (memory)) { - filtered.Add (new Unix2DosFilter ()); - - entity.WriteTo (filtered); - filtered.Flush (); - } - - memory.Position = 0; - - var encrypted = new MultipartEncrypted (); - encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; - - // add the protocol version part - encrypted.Add (new ApplicationPgpEncrypted ()); - - // add the encrypted entity as the second part - encrypted.Add (ctx.Encrypt (algorithm, recipients, memory)); - - return encrypted; - } - } - - /// - /// Create a multipart/encrypted MIME part by encrypting the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients, encapsulating the result in a - /// new multipart/encrypted part. - /// - /// A new instance containing - /// the encrypted version of the specified entity. - /// The OpenPGP cryptography context to use for encrypting. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// A public key for one or more of the could not be found. - /// - public static MultipartEncrypted Encrypt (OpenPgpContext ctx, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - using (var filtered = new FilteredStream (memory)) { - filtered.Add (new Unix2DosFilter ()); - - entity.WriteTo (filtered); - filtered.Flush (); - } - - memory.Position = 0; - - var encrypted = new MultipartEncrypted (); - encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; - - // add the protocol version part - encrypted.Add (new ApplicationPgpEncrypted ()); - - // add the encrypted entity as the second part - encrypted.Add (ctx.Encrypt (recipients, memory)); - - return encrypted; - } - } - - /// - /// Create a multipart/encrypted MIME part by encrypting the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients, encapsulating the result in a - /// new multipart/encrypted part. - /// - /// A new instance containing - /// the encrypted version of the specified entity. - /// The encryption algorithm. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// - /// - /// A default has not been registered. - /// -or- - /// The specified encryption algorithm is not supported. - /// - public static MultipartEncrypted Encrypt (EncryptionAlgorithm algorithm, IEnumerable recipients, MimeEntity entity) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-encrypted")) - return Encrypt (ctx, algorithm, recipients, entity); - } - - /// - /// Create a multipart/encrypted MIME part by encrypting the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients, encapsulating the result in a - /// new multipart/encrypted part. - /// - /// A new instance containing - /// the encrypted version of the specified entity. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// A default has not been registered. - /// - /// - /// A public key for one or more of the could not be found. - /// - public static MultipartEncrypted Encrypt (IEnumerable recipients, MimeEntity entity) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-encrypted")) - return Encrypt (ctx, recipients, entity); - } - - /// - /// Create a multipart/encrypted MIME part by encrypting the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients, encapsulating the result in a - /// new multipart/encrypted part. - /// - /// A new instance containing - /// the encrypted version of the specified entity. - /// The OpenPGP cryptography context to use for encrypting. - /// The encryption algorithm. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// - /// - /// THe specified encryption algorithm is not supported. - /// - public static MultipartEncrypted Encrypt (OpenPgpContext ctx, EncryptionAlgorithm algorithm, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - using (var filtered = new FilteredStream (memory)) { - filtered.Add (new Unix2DosFilter ()); - - entity.WriteTo (filtered); - filtered.Flush (); - } - - memory.Position = 0; - - var encrypted = new MultipartEncrypted (); - encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; - - // add the protocol version part - encrypted.Add (new ApplicationPgpEncrypted ()); - - // add the encrypted entity as the second part - encrypted.Add (ctx.Encrypt (algorithm, recipients, memory)); - - return encrypted; - } - } - - /// - /// Create a multipart/encrypted MIME part by encrypting the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients, encapsulating the result in a - /// new multipart/encrypted part. - /// - /// A new instance containing - /// the encrypted version of the specified entity. - /// The OpenPGP cryptography context to use for encrypting. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// - public static MultipartEncrypted Encrypt (OpenPgpContext ctx, IEnumerable recipients, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - using (var filtered = new FilteredStream (memory)) { - filtered.Add (new Unix2DosFilter ()); - - entity.WriteTo (filtered); - filtered.Flush (); - } - - memory.Position = 0; - - var encrypted = new MultipartEncrypted (); - encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; - - // add the protocol version part - encrypted.Add (new ApplicationPgpEncrypted ()); - - // add the encrypted entity as the second part - encrypted.Add (ctx.Encrypt (recipients, memory)); - - return encrypted; - } - } - - /// - /// Create a multipart/encrypted MIME part by encrypting the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients, encapsulating the result in a - /// new multipart/encrypted part. - /// - /// A new instance containing - /// the encrypted version of the specified entity. - /// The encryption algorithm. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// - /// - /// A default has not been registered. - /// -or- - /// The specified encryption algorithm is not supported. - /// - public static MultipartEncrypted Encrypt (EncryptionAlgorithm algorithm, IEnumerable recipients, MimeEntity entity) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-encrypted")) - return Encrypt (ctx, algorithm, recipients, entity); - } - - /// - /// Create a multipart/encrypted MIME part by encrypting the specified entity. - /// - /// - /// Encrypts the entity to the specified recipients, encapsulating the result in a - /// new multipart/encrypted part. - /// - /// A new instance containing - /// the encrypted version of the specified entity. - /// The recipients for the encrypted entity. - /// The entity to sign and encrypt. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// - /// - /// A default has not been registered. - /// - public static MultipartEncrypted Encrypt (IEnumerable recipients, MimeEntity entity) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-encrypted")) - return Encrypt (ctx, recipients, entity); - } - - /// - /// Decrypts the part. - /// - /// - /// Decrypts the and extracts any digital signatures in cases - /// where the content was also signed. - /// - /// The decrypted entity. - /// The OpenPGP cryptography context to use for decrypting. - /// A list of digital signatures if the data was both signed and encrypted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The protocol parameter was not specified. - /// -or- - /// The multipart is malformed in some way. - /// - /// - /// The provided does not support the protocol parameter. - /// - /// - /// The private key could not be found to decrypt the encrypted data. - /// - /// - /// The user chose to cancel the password prompt. - /// -or- - /// The operation was cancelled via the cancellation token. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public MimeEntity Decrypt (OpenPgpContext ctx, out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - var protocol = ContentType.Parameters["protocol"]?.Trim (); - if (string.IsNullOrEmpty (protocol)) - throw new FormatException (); - - if (!ctx.Supports (protocol)) - throw new NotSupportedException (); - - if (Count < 2) - throw new FormatException (); - - var version = this[0] as MimePart; - if (version == null) - throw new FormatException (); - - var ctype = version.ContentType; - var value = string.Format ("{0}/{1}", ctype.MediaType, ctype.MediaSubtype); - if (!value.Equals (protocol, StringComparison.OrdinalIgnoreCase)) - throw new FormatException (); - - var encrypted = this[1] as MimePart; - if (encrypted == null || encrypted.Content == null) - throw new FormatException (); - - if (!encrypted.ContentType.IsMimeType ("application", "octet-stream")) - throw new FormatException (); - - using (var memory = new MemoryBlockStream ()) { - encrypted.Content.DecodeTo (memory, cancellationToken); - memory.Position = 0; - - return ctx.Decrypt (memory, out signatures, cancellationToken); - } - } - - /// - /// Decrypts the part. - /// - /// - /// Decrypts the part. - /// - /// The decrypted entity. - /// The OpenPGP cryptography context to use for decrypting. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The protocol parameter was not specified. - /// -or- - /// The multipart is malformed in some way. - /// - /// - /// The provided does not support the protocol parameter. - /// - /// - /// The private key could not be found to decrypt the encrypted data. - /// - /// - /// The user chose to cancel the password prompt. - /// -or- - /// The operation was cancelled via the cancellation token. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public MimeEntity Decrypt (OpenPgpContext ctx, CancellationToken cancellationToken = default (CancellationToken)) - { - return Decrypt (ctx, out _, cancellationToken); - } - - /// - /// Decrypts the part. - /// - /// - /// Decrypts the and extracts any digital signatures in cases - /// where the content was also signed. - /// - /// The decrypted entity. - /// A list of digital signatures if the data was both signed and encrypted. - /// The cancellation token. - /// - /// The protocol parameter was not specified. - /// -or- - /// The multipart is malformed in some way. - /// - /// - /// A suitable for - /// decrypting could not be found. - /// - /// - /// The private key could not be found to decrypt the encrypted data. - /// - /// - /// The user chose to cancel the password prompt. - /// -or- - /// The operation was cancelled via the cancellation token. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public MimeEntity Decrypt (out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) - { - var protocol = ContentType.Parameters["protocol"]?.Trim (); - if (string.IsNullOrEmpty (protocol)) - throw new FormatException (); - - if (Count < 2) - throw new FormatException (); - - var version = this[0] as MimePart; - if (version == null) - throw new FormatException (); - - var ctype = version.ContentType; - var value = string.Format ("{0}/{1}", ctype.MediaType, ctype.MediaSubtype); - if (!value.Equals (protocol, StringComparison.OrdinalIgnoreCase)) - throw new FormatException (); - - var encrypted = this[1] as MimePart; - if (encrypted == null || encrypted.Content == null) - throw new FormatException (); - - if (!encrypted.ContentType.IsMimeType ("application", "octet-stream")) - throw new FormatException (); - - using (var ctx = CryptographyContext.Create (protocol)) { - using (var memory = new MemoryBlockStream ()) { - var pgp = ctx as OpenPgpContext; - - encrypted.Content.DecodeTo (memory, cancellationToken); - memory.Position = 0; - - if (pgp != null) - return pgp.Decrypt (memory, out signatures, cancellationToken); - - signatures = null; - - return ctx.Decrypt (memory, cancellationToken); - } - } - } - - /// - /// Decrypts the part. - /// - /// - /// Decrypts the part. - /// - /// The decrypted entity. - /// The cancellation token. - /// - /// The protocol parameter was not specified. - /// -or- - /// The multipart is malformed in some way. - /// - /// - /// A suitable for - /// decrypting could not be found. - /// - /// - /// The private key could not be found to decrypt the encrypted data. - /// - /// - /// The user chose to cancel the password prompt. - /// -or- - /// The operation was cancelled via the cancellation token. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public MimeEntity Decrypt (CancellationToken cancellationToken = default (CancellationToken)) - { - return Decrypt (out _, cancellationToken); - } - } -} diff --git a/src/MimeKit/Cryptography/MultipartSigned.cs b/src/MimeKit/Cryptography/MultipartSigned.cs deleted file mode 100644 index 04b30c0..0000000 --- a/src/MimeKit/Cryptography/MultipartSigned.cs +++ /dev/null @@ -1,581 +0,0 @@ -// -// MultipartSigned.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -using Org.BouncyCastle.Bcpg.OpenPgp; - -using MimeKit.IO; -using MimeKit.IO.Filters; - -namespace MimeKit.Cryptography { - /// - /// A signed multipart, as used by both S/MIME and PGP/MIME protocols. - /// - /// - /// The first child of a multipart/signed is the content while the second child - /// is the detached signature data. Any other children are not defined and could - /// be anything. - /// - public class MultipartSigned : Multipart - { - /// - /// Initialize a new instance of the class. - /// - /// This constructor is used by . - /// Information used by the constructor. - /// - /// is null. - /// - public MultipartSigned (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public MultipartSigned () : base ("signed") - { - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMultipartSigned (this); - } - - static MimeEntity Prepare (MimeEntity entity, Stream memory) - { - entity.Prepare (EncodingConstraint.SevenBit, 78); - - using (var filtered = new FilteredStream (memory)) { - // Note: see rfc3156, section 3 - second note - filtered.Add (new ArmoredFromFilter ()); - - // Note: see rfc3156, section 5.4 (this is the main difference between rfc2015 and rfc3156) - filtered.Add (new TrailingWhitespaceFilter ()); - - // Note: see rfc2015 or rfc3156, section 5.1 - filtered.Add (new Unix2DosFilter ()); - - entity.WriteTo (filtered); - filtered.Flush (); - } - - memory.Position = 0; - - // Note: we need to parse the modified entity structure to preserve any modifications - var parser = new MimeParser (memory, MimeFormat.Entity); - - return parser.ParseEntity (); - } - - static MultipartSigned Create (CryptographyContext ctx, DigestAlgorithm digestAlgo, MimeEntity entity, MimeEntity signature) - { - var micalg = ctx.GetDigestAlgorithmName (digestAlgo); - var signed = new MultipartSigned (); - - // set the protocol and micalg Content-Type parameters - signed.ContentType.Parameters["protocol"] = ctx.SignatureProtocol; - signed.ContentType.Parameters["micalg"] = micalg; - - // add the modified/parsed entity as our first part - signed.Add (entity); - - // add the detached signature as the second part - signed.Add (signature); - - return signed; - } - - /// - /// Creates a new . - /// - /// - /// Cryptographically signs the entity using the supplied signer and digest algorithm in - /// order to generate a detached signature and then adds the entity along with the - /// detached signature data to a new multipart/signed part. - /// - /// A new instance. - /// The cryptography context to use for signing. - /// The signer. - /// The digest algorithm to use for signing. - /// The entity to sign. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// - /// - /// A signing certificate could not be found for . - /// - /// - /// The private key could not be found for . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static MultipartSigned Create (CryptographyContext ctx, MailboxAddress signer, DigestAlgorithm digestAlgo, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var prepared = Prepare (entity, memory); - - memory.Position = 0; - - // sign the cleartext content - var signature = ctx.Sign (signer, digestAlgo, memory); - - return Create (ctx, digestAlgo, prepared, signature); - } - } - - /// - /// Creates a new . - /// - /// - /// Cryptographically signs the entity using the supplied signer and digest algorithm in - /// order to generate a detached signature and then adds the entity along with the - /// detached signature data to a new multipart/signed part. - /// - /// A new instance. - /// The OpenPGP context to use for signing. - /// The signer. - /// The digest algorithm to use for signing. - /// The entity to sign. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// - /// - /// An error occurred in the OpenPGP subsystem. - /// - public static MultipartSigned Create (OpenPgpContext ctx, PgpSecretKey signer, DigestAlgorithm digestAlgo, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var prepared = Prepare (entity, memory); - - memory.Position = 0; - - // sign the cleartext content - var signature = ctx.Sign (signer, digestAlgo, memory); - - return Create (ctx, digestAlgo, prepared, signature); - } - } - - /// - /// Creates a new . - /// - /// - /// Cryptographically signs the entity using the supplied signer and digest algorithm in - /// order to generate a detached signature and then adds the entity along with the - /// detached signature data to a new multipart/signed part. - /// - /// A new instance. - /// The signer. - /// The digest algorithm to use for signing. - /// The entity to sign. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// - /// - /// The was out of range. - /// - /// - /// A cryptography context suitable for signing could not be found. - /// -or- - /// The is not supported. - /// - /// - /// An error occurred in the OpenPGP subsystem. - /// - public static MultipartSigned Create (PgpSecretKey signer, DigestAlgorithm digestAlgo, MimeEntity entity) - { - using (var ctx = (OpenPgpContext) CryptographyContext.Create ("application/pgp-signature")) - return Create (ctx, signer, digestAlgo, entity); - } - - /// - /// Creates a new . - /// - /// - /// Cryptographically signs the entity using the supplied signer in order - /// to generate a detached signature and then adds the entity along with - /// the detached signature data to a new multipart/signed part. - /// - /// A new instance. - /// The S/MIME context to use for signing. - /// The signer. - /// The entity to sign. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static MultipartSigned Create (SecureMimeContext ctx, CmsSigner signer, MimeEntity entity) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (entity == null) - throw new ArgumentNullException (nameof (entity)); - - using (var memory = new MemoryBlockStream ()) { - var prepared = Prepare (entity, memory); - - memory.Position = 0; - - // sign the cleartext content - var signature = ctx.Sign (signer, memory); - - return Create (ctx, signer.DigestAlgorithm, prepared, signature); - } - } - - /// - /// Creates a new . - /// - /// - /// Cryptographically signs the entity using the supplied signer in order - /// to generate a detached signature and then adds the entity along with - /// the detached signature data to a new multipart/signed part. - /// - /// A new instance. - /// The signer. - /// The entity to sign. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// A cryptography context suitable for signing could not be found. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public static MultipartSigned Create (CmsSigner signer, MimeEntity entity) - { - using (var ctx = (SecureMimeContext) CryptographyContext.Create ("application/pkcs7-signature")) - return Create (ctx, signer, entity); - } - - /// - /// Prepare the MIME entity for transport using the specified encoding constraints. - /// - /// - /// Prepares the MIME entity for transport using the specified encoding constraints. - /// - /// The encoding constraint. - /// The maximum number of octets allowed per line (not counting the CRLF). Must be between 60 and 998 (inclusive). - /// - /// is not between 60 and 998 (inclusive). - /// -or- - /// is not a valid value. - /// - public override void Prepare (EncodingConstraint constraint, int maxLineLength = 78) - { - if (maxLineLength < FormatOptions.MinimumLineLength || maxLineLength > FormatOptions.MaximumLineLength) - throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - - // Note: we do not iterate over our children because they are already signed - // and changing them would break the signature. They should already be - // properly prepared, anyway. - } - - /// - /// Verify the multipart/signed part. - /// - /// - /// Verifies the multipart/signed part using the supplied cryptography context. - /// - /// A signer info collection. - /// The cryptography context to use for verifying the signature. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The multipart is malformed in some way. - /// - /// - /// does not support verifying the signature part. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public DigitalSignatureCollection Verify (CryptographyContext ctx, CancellationToken cancellationToken = default (CancellationToken)) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - var protocol = ContentType.Parameters["protocol"]?.Trim (); - if (string.IsNullOrEmpty (protocol)) - throw new FormatException ("The multipart/signed part did not specify a protocol."); - - if (!ctx.Supports (protocol)) - throw new NotSupportedException ("The specified cryptography context does not support the signature protocol."); - - if (Count < 2) - throw new FormatException ("The multipart/signed part did not contain the expected children."); - - var signature = this[1] as MimePart; - if (signature == null || signature.Content == null) - throw new FormatException ("The signature part could not be found."); - - var ctype = signature.ContentType; - var value = string.Format ("{0}/{1}", ctype.MediaType, ctype.MediaSubtype); - if (!ctx.Supports (value)) - throw new NotSupportedException (string.Format ("The specified cryptography context does not support '{0}'.", value)); - - using (var signatureData = new MemoryBlockStream ()) { - signature.Content.DecodeTo (signatureData, cancellationToken); - signatureData.Position = 0; - - using (var cleartext = new MemoryBlockStream ()) { - // Note: see rfc2015 or rfc3156, section 5.1 - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - options.VerifyingSignature = true; - - this[0].WriteTo (options, cleartext); - cleartext.Position = 0; - - return ctx.Verify (cleartext, signatureData, cancellationToken); - } - } - } - - /// - /// Verify the multipart/signed part. - /// - /// - /// Verifies the multipart/signed part using the supplied cryptography context. - /// - /// A signer info collection. - /// The cryptography context to use for verifying the signature. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The multipart is malformed in some way. - /// - /// - /// does not support verifying the signature part. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public async Task VerifyAsync (CryptographyContext ctx, CancellationToken cancellationToken = default (CancellationToken)) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - var protocol = ContentType.Parameters["protocol"]?.Trim (); - if (string.IsNullOrEmpty (protocol)) - throw new FormatException ("The multipart/signed part did not specify a protocol."); - - if (!ctx.Supports (protocol)) - throw new NotSupportedException ("The specified cryptography context does not support the signature protocol."); - - if (Count < 2) - throw new FormatException ("The multipart/signed part did not contain the expected children."); - - var signature = this[1] as MimePart; - if (signature == null || signature.Content == null) - throw new FormatException ("The signature part could not be found."); - - var ctype = signature.ContentType; - var value = string.Format ("{0}/{1}", ctype.MediaType, ctype.MediaSubtype); - if (!ctx.Supports (value)) - throw new NotSupportedException (string.Format ("The specified cryptography context does not support '{0}'.", value)); - - using (var signatureData = new MemoryBlockStream ()) { - await signature.Content.DecodeToAsync (signatureData, cancellationToken).ConfigureAwait (false); - signatureData.Position = 0; - - using (var cleartext = new MemoryBlockStream ()) { - // Note: see rfc2015 or rfc3156, section 5.1 - var options = FormatOptions.CloneDefault (); - options.NewLineFormat = NewLineFormat.Dos; - options.VerifyingSignature = true; - - await this[0].WriteToAsync (options, cleartext, cancellationToken); - cleartext.Position = 0; - - return await ctx.VerifyAsync (cleartext, signatureData, cancellationToken).ConfigureAwait (false); - } - } - } - - /// - /// Verify the multipart/signed part. - /// - /// - /// Verifies the multipart/signed part using the default cryptography context. - /// - /// A signer info collection. - /// The cancellation token. - /// - /// The protocol parameter was not specified. - /// -or- - /// The multipart is malformed in some way. - /// - /// - /// A cryptography context suitable for verifying the signature could not be found. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public DigitalSignatureCollection Verify (CancellationToken cancellationToken = default (CancellationToken)) - { - var protocol = ContentType.Parameters["protocol"]?.Trim (); - - if (string.IsNullOrEmpty (protocol)) - throw new FormatException ("The multipart/signed part did not specify a protocol."); - - using (var ctx = CryptographyContext.Create (protocol)) - return Verify (ctx, cancellationToken); - } - - /// - /// Asynchronously verify the multipart/signed part. - /// - /// - /// Verifies the multipart/signed part using the default cryptography context. - /// - /// A signer info collection. - /// The cancellation token. - /// - /// The protocol parameter was not specified. - /// -or- - /// The multipart is malformed in some way. - /// - /// - /// A cryptography context suitable for verifying the signature could not be found. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public Task VerifyAsync (CancellationToken cancellationToken = default (CancellationToken)) - { - var protocol = ContentType.Parameters["protocol"]?.Trim (); - - if (string.IsNullOrEmpty (protocol)) - throw new FormatException ("The multipart/signed part did not specify a protocol."); - - using (var ctx = CryptographyContext.Create (protocol)) - return VerifyAsync (ctx, cancellationToken); - } - } -} diff --git a/src/MimeKit/Cryptography/NpgsqlCertificateDatabase.cs b/src/MimeKit/Cryptography/NpgsqlCertificateDatabase.cs deleted file mode 100644 index 1bee953..0000000 --- a/src/MimeKit/Cryptography/NpgsqlCertificateDatabase.cs +++ /dev/null @@ -1,264 +0,0 @@ -// -// NpgsqlCertificateDatabase.cs -// -// Author: Federico Di Gregorio -// -// Copyright (c) 2013-2019 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Data; -using System.Text; -using System.Reflection; -using System.Data.Common; -using System.Collections.Generic; - -namespace MimeKit.Cryptography { - /// - /// An X.509 certificate database built on PostgreSQL. - /// - /// - /// An X.509 certificate database is used for storing certificates, metdata related to the certificates - /// (such as encryption algorithms supported by the associated client), certificate revocation lists (CRLs), - /// and private keys. - /// This particular database uses PostgreSQL to store the data. - /// - public class NpgsqlCertificateDatabase : SqlCertificateDatabase - { - static readonly Type npgsqlConnectionClass; - static readonly Assembly npgsqlAssembly; - - static NpgsqlCertificateDatabase () - { - try { - npgsqlAssembly = Assembly.Load ("Npgsql"); - if (npgsqlAssembly != null) { - npgsqlConnectionClass = npgsqlAssembly.GetType ("Npgsql.NpgsqlConnection"); - - IsAvailable = true; - } - } catch (FileNotFoundException) { - } catch (FileLoadException) { - } catch (BadImageFormatException) { - } - } - - internal static bool IsAvailable { - get; private set; - } - - static DbConnection CreateConnection (string connectionString) - { - if (connectionString == null) - throw new ArgumentNullException (nameof (connectionString)); - - if (connectionString.Length == 0) - throw new ArgumentException ("The connection string cannot be empty.", nameof (connectionString)); - - return (DbConnection) Activator.CreateInstance (npgsqlConnectionClass, new [] { connectionString }); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new and opens a connection to the - /// PostgreSQL database using the specified connection string. - /// - /// The connection string. - /// The password used for encrypting and decrypting the private keys. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// - public NpgsqlCertificateDatabase (string connectionString, string password) : base (CreateConnection (connectionString), password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Creates a new using the provided Npgsql database connection. - /// - /// The Npgsql connection. - /// The password used for encrypting and decrypting the private keys. - /// - /// is null. - /// -or- - /// is null. - /// - public NpgsqlCertificateDatabase (DbConnection connection, string password) : base (connection, password) - { - } - - /// - /// Gets the columns for the specified table. - /// - /// - /// Gets the list of columns for the specified table. - /// - /// The . - /// The name of the table. - /// The list of columns. - protected override IList GetTableColumns (DbConnection connection, string tableName) - { - using (var command = connection.CreateCommand ()) { - command.CommandText = $"PRAGMA table_info({tableName})"; - using (var reader = command.ExecuteReader ()) { - var columns = new List (); - - while (reader.Read ()) { - var column = new DataColumn (); - - for (int i = 0; i < reader.FieldCount; i++) { - var field = reader.GetName (i).ToUpperInvariant (); - - switch (field) { - case "NAME": - column.ColumnName = reader.GetString (i); - break; - case "TYPE": - var type = reader.GetString (i); - switch (type) { - case "boolean": column.DataType = typeof (bool); break; - case "integer": column.DataType = typeof (long); break; - case "bytea": column.DataType = typeof (byte[]); break; - case "text": column.DataType = typeof (string); break; - } - break; - case "NOTNULL": - column.AllowDBNull = !reader.GetBoolean (i); - break; - } - } - - columns.Add (column); - } - - return columns; - } - } - } - - static void Build (StringBuilder statement, DataTable table, DataColumn column, ref int primaryKeys) - { - statement.Append (column.ColumnName); - statement.Append (' '); - - if (column.DataType == typeof (long) || column.DataType == typeof (int)) { - if (column.AutoIncrement) - statement.Append ("serial"); - else - statement.Append ("integer"); - } else if (column.DataType == typeof (bool)) { - statement.Append ("boolean"); - } else if (column.DataType == typeof (byte[])) { - statement.Append ("bytea"); - } else if (column.DataType == typeof (string)) { - statement.Append ("text"); - } else { - throw new NotImplementedException (); - } - - bool isPrimaryKey = false; - if (table != null && table.PrimaryKey != null && primaryKeys < table.PrimaryKey.Length) { - for (int i = 0; i < table.PrimaryKey.Length; i++) { - if (column == table.PrimaryKey[i]) { - statement.Append (" PRIMARY KEY"); - isPrimaryKey = true; - primaryKeys++; - break; - } - } - } - - if (column.Unique && !isPrimaryKey) - statement.Append (" UNIQUE"); - - if (!column.AllowDBNull) - statement.Append (" NOT NULL"); - } - - /// - /// Create a table. - /// - /// - /// Creates the specified table. - /// - /// The . - /// The table. - protected override void CreateTable (DbConnection connection, DataTable table) - { - var statement = new StringBuilder ("CREATE TABLE IF NOT EXISTS "); - int primaryKeys = 0; - - statement.Append (table.TableName); - statement.Append ('('); - - foreach (DataColumn column in table.Columns) { - Build (statement, table, column, ref primaryKeys); - statement.Append (", "); - } - - if (table.Columns.Count > 0) - statement.Length -= 2; - - statement.Append (')'); - - using (var command = connection.CreateCommand ()) { - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; - command.ExecuteNonQuery (); - } - } - - /// - /// Adds a column to a table. - /// - /// - /// Adds a column to a table. - /// - /// The . - /// The table. - /// The column to add. - protected override void AddTableColumn (DbConnection connection, DataTable table, DataColumn column) - { - var statement = new StringBuilder ("ALTER TABLE "); - int primaryKeys = table.PrimaryKey?.Length ?? 0; - - statement.Append (table.TableName); - statement.Append (" ADD COLUMN "); - Build (statement, table, column, ref primaryKeys); - - using (var command = connection.CreateCommand ()) { - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; - command.ExecuteNonQuery (); - } - } - } -} diff --git a/src/MimeKit/Cryptography/OpenPgpBlockFilter.cs b/src/MimeKit/Cryptography/OpenPgpBlockFilter.cs deleted file mode 100644 index d5c109b..0000000 --- a/src/MimeKit/Cryptography/OpenPgpBlockFilter.cs +++ /dev/null @@ -1,208 +0,0 @@ -// -// OpenPgpArmoredFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MimeKit.Utils; -using MimeKit.IO.Filters; - -namespace MimeKit.Cryptography { - /// - /// A filter to strip off data before and after an armored OpenPGP block. - /// - /// - /// Filters out data before and after armored OpenPGP blocks. - /// - class OpenPgpBlockFilter : MimeFilterBase - { - readonly byte[] beginMarker; - readonly byte[] endMarker; - bool seenBeginMarker; - bool seenEndMarker; - bool midline; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// An OpenPGP begin marker. - /// An OpenPGP end marker. - public OpenPgpBlockFilter (string beginMarker, string endMarker) - { - this.beginMarker = CharsetUtils.UTF8.GetBytes (beginMarker); - this.endMarker = CharsetUtils.UTF8.GetBytes (endMarker); - } - - static bool IsMarker (byte[] input, int startIndex, byte[] marker) - { - int i = startIndex; - - for (int j = 0; j < marker.Length; i++, j++) { - if (input[i] != marker[j]) - return false; - } - - if (input[i] == (byte) '\r') - i++; - - return input[i] == (byte) '\n'; - } - - static bool IsPartialMatch (byte[] input, int startIndex, int endIndex, byte[] marker) - { - int i = startIndex; - - for (int j = 0; j < marker.Length && i < endIndex; i++, j++) { - if (input[i] != marker[j]) - return false; - } - - if (i < endIndex && input[i] == (byte) '\r') - i++; - - return i == endIndex; - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - int endIndex = startIndex + length; - int index = startIndex; - - outputIndex = startIndex; - outputLength = 0; - - if (seenEndMarker || length == 0) - return input; - - if (midline) { - while (index < endIndex && input[index] != (byte) '\n') - index++; - - if (index == endIndex) { - if (seenBeginMarker) - outputLength = index - startIndex; - - return input; - } - - midline = false; - } - - if (!seenBeginMarker) { - do { - int lineIndex = index; - - while (index < endIndex && input[index] != (byte) '\n') - index++; - - if (index == endIndex) { - if (IsPartialMatch (input, lineIndex, index, beginMarker)) - SaveRemainingInput (input, lineIndex, index - lineIndex); - else - midline = true; - return input; - } - - index++; - - if (IsMarker (input, lineIndex, beginMarker)) { - outputLength = index - lineIndex; - outputIndex = lineIndex; - seenBeginMarker = true; - break; - } - } while (index < endIndex); - - if (index == endIndex) - return input; - } - - do { - int lineIndex = index; - - while (index < endIndex && input[index] != (byte) '\n') - index++; - - if (index == endIndex) { - if (!flush) { - if (IsPartialMatch (input, lineIndex, index, endMarker)) { - SaveRemainingInput (input, lineIndex, index - lineIndex); - outputLength = lineIndex - outputIndex; - } else { - outputLength = index - outputIndex; - midline = true; - } - - return input; - } - - outputLength = index - outputIndex; - return input; - } - - index++; - - if (IsMarker (input, lineIndex, endMarker)) { - seenEndMarker = true; - break; - } - } while (index < endIndex); - - outputLength = index - outputIndex; - - return input; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - seenBeginMarker = false; - seenEndMarker = false; - midline = false; - - base.Reset (); - } - } -} diff --git a/src/MimeKit/Cryptography/OpenPgpContext.cs b/src/MimeKit/Cryptography/OpenPgpContext.cs deleted file mode 100644 index 37c504d..0000000 --- a/src/MimeKit/Cryptography/OpenPgpContext.cs +++ /dev/null @@ -1,1185 +0,0 @@ -// -// OpenPgpContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Diagnostics; -using System.Threading.Tasks; -using System.Collections.Generic; - -using Org.BouncyCastle.Bcpg; -using Org.BouncyCastle.Math; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Crypto.Prng; -using Org.BouncyCastle.Bcpg.OpenPgp; -using Org.BouncyCastle.Crypto.Parameters; - -using MimeKit.IO; -using MimeKit.Utils; - -namespace MimeKit.Cryptography { - /// - /// An abstract OpenPGP cryptography context which can be used for OpenPGP and PGP/MIME that - /// manages keyrings stored on the local file system as keyring bundles. - /// - /// - /// PGP software such as older versions of GnuPG (pre 2.1.0) typically store the user's - /// keyrings on the file system using the OpenPGP Keyring Bundle format. - /// Generally speaking, applications should not use a - /// directly, but rather via higher level APIs such as - /// and . - /// - public abstract class OpenPgpContext : OpenPgpContextBase - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Subclasses choosing to use this constructor MUST set the , - /// , , and the - /// properties themselves. - /// - protected OpenPgpContext () : base () - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new using the specified public and private keyring paths. - /// - /// The public keyring file path. - /// The secret keyring file path. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred while reading one of the keyring files. - /// - /// - /// An error occurred while parsing one of the keyring files. - /// - protected OpenPgpContext (string pubring, string secring) : this () - { - if (pubring == null) - throw new ArgumentNullException (nameof (pubring)); - - if (secring == null) - throw new ArgumentNullException (nameof (secring)); - - PublicKeyRingPath = pubring; - SecretKeyRingPath = secring; - - if (File.Exists (pubring)) { - using (var file = File.OpenRead (pubring)) { - PublicKeyRingBundle = new PgpPublicKeyRingBundle (file); - } - } else { - PublicKeyRingBundle = new PgpPublicKeyRingBundle (new byte[0]); - } - - if (File.Exists (secring)) { - using (var file = File.OpenRead (secring)) { - SecretKeyRingBundle = new PgpSecretKeyRingBundle (file); - } - } else { - SecretKeyRingBundle = new PgpSecretKeyRingBundle (new byte[0]); - } - } - - /// - /// Get the public keyring path. - /// - /// - /// Gets the public keyring path. - /// - /// The public key ring path. - protected string PublicKeyRingPath { - get; set; - } - - /// - /// Get the secret keyring path. - /// - /// - /// Gets the secret keyring path. - /// - /// The secret key ring path. - protected string SecretKeyRingPath { - get; set; - } - - /// - /// Get the public keyring bundle. - /// - /// - /// Gets the public keyring bundle. - /// - /// The public keyring bundle. - public PgpPublicKeyRingBundle PublicKeyRingBundle { - get; protected set; - } - - /// - /// Get the secret keyring bundle. - /// - /// - /// Gets the secret keyring bundle. - /// - /// The secret keyring bundle. - public PgpSecretKeyRingBundle SecretKeyRingBundle { - get; protected set; - } - - bool TryGetPublicKeyRing (long keyId, out PgpPublicKeyRing keyring) - { - foreach (PgpPublicKeyRing ring in PublicKeyRingBundle.GetKeyRings ()) { - foreach (PgpPublicKey key in ring.GetPublicKeys ()) { - if (key.KeyId == keyId) { - keyring = ring; - return true; - } - } - } - - keyring = null; - - return false; - } - - /// - /// Get the public keyring that contains the specified key. - /// - /// - /// Gets the public keyring that contains the specified key. - /// Implementations should first try to obtain the keyring stored (or cached) locally. - /// Failing that, if is enabled, they should use - /// to attempt to - /// retrieve the keyring from the configured . - /// - /// The public key identifier. - /// The cancellation token. - /// The public keyring that contains the specified key or null if the keyring could not be found. - /// - /// The operation was cancelled. - /// - protected override PgpPublicKeyRing GetPublicKeyRing (long keyId, CancellationToken cancellationToken) - { - if (TryGetPublicKeyRing (keyId, out var keyring)) - return keyring; - - if (AutoKeyRetrieve) - return RetrievePublicKeyRing (keyId, cancellationToken); - - return null; - } - - /// - /// Asynchronously get the public keyring that contains the specified key. - /// - /// - /// Gets the public keyring that contains the specified key. - /// Implementations should first try to obtain the keyring stored (or cached) locally. - /// Failing that, if is enabled, they should use - /// to attempt to - /// retrieve the keyring from the configured . - /// - /// The public key identifier. - /// The cancellation token. - /// The public keyring that contains the specified key or null if the keyring could not be found. - /// - /// The operation was cancelled. - /// - protected override async Task GetPublicKeyRingAsync (long keyId, CancellationToken cancellationToken) - { - if (TryGetPublicKeyRing (keyId, out var keyring)) - return keyring; - - if (AutoKeyRetrieve) - return await RetrievePublicKeyRingAsync (keyId, cancellationToken).ConfigureAwait (false); - - return null; - } - - /// - /// Enumerate all public keyrings. - /// - /// - /// Enumerates all public keyrings. - /// - /// The list of available public keyrings. - public virtual IEnumerable EnumeratePublicKeyRings () - { - foreach (PgpPublicKeyRing keyring in PublicKeyRingBundle.GetKeyRings ()) - yield return keyring; - - yield break; - } - - /// - /// Enumerate all public keys. - /// - /// - /// Enumerates all public keys. - /// - /// The list of available public keys. - public virtual IEnumerable EnumeratePublicKeys () - { - foreach (var keyring in EnumeratePublicKeyRings ()) { - foreach (PgpPublicKey key in keyring.GetPublicKeys ()) - yield return key; - } - - yield break; - } - - /// - /// Enumerate the public keyrings for a particular mailbox. - /// - /// - /// Enumerates all public keyrings for the specified mailbox. - /// - /// The public keys. - /// Mailbox. - /// - /// is null. - /// - public virtual IEnumerable EnumeratePublicKeyRings (MailboxAddress mailbox) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - foreach (var keyring in EnumeratePublicKeyRings ()) { - if (IsMatch (keyring.GetPublicKey (), mailbox)) - yield return keyring; - } - - yield break; - } - - /// - /// Enumerate the public keys for a particular mailbox. - /// - /// - /// Enumerates all public keys for the specified mailbox. - /// - /// The public keys. - /// The mailbox address. - /// - /// is null. - /// - public virtual IEnumerable EnumeratePublicKeys (MailboxAddress mailbox) - { - foreach (var keyring in EnumeratePublicKeyRings (mailbox)) { - foreach (PgpPublicKey key in keyring.GetPublicKeys ()) - yield return key; - } - - yield break; - } - - /// - /// Enumerate all secret keyrings. - /// - /// - /// Enumerates all secret keyrings. - /// - /// The list of available secret keyrings. - public virtual IEnumerable EnumerateSecretKeyRings () - { - foreach (PgpSecretKeyRing keyring in SecretKeyRingBundle.GetKeyRings ()) - yield return keyring; - - yield break; - } - - /// - /// Enumerate all secret keys. - /// - /// - /// Enumerates all secret keys. - /// - /// The list of available secret keys. - public virtual IEnumerable EnumerateSecretKeys () - { - foreach (var keyring in EnumerateSecretKeyRings ()) { - foreach (PgpSecretKey key in keyring.GetSecretKeys ()) - yield return key; - } - - yield break; - } - - /// - /// Enumerate the secret keyrings for a particular mailbox. - /// - /// - /// Enumerates all secret keyrings for the specified mailbox. - /// - /// The secret keys. - /// The mailbox address. - /// - /// is null. - /// - public virtual IEnumerable EnumerateSecretKeyRings (MailboxAddress mailbox) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - foreach (var keyring in EnumerateSecretKeyRings ()) { - if (IsMatch (keyring.GetSecretKey (), mailbox)) - yield return keyring; - } - - yield break; - } - - /// - /// Enumerate the secret keys for a particular mailbox. - /// - /// - /// Enumerates all secret keys for the specified mailbox. - /// - /// The public keys. - /// The mailbox address. - /// - /// is null. - /// - public virtual IEnumerable EnumerateSecretKeys (MailboxAddress mailbox) - { - foreach (var keyring in EnumerateSecretKeyRings (mailbox)) { - foreach (PgpSecretKey key in keyring.GetSecretKeys ()) - yield return key; - } - - yield break; - } - - /// - /// Get the public key associated with the mailbox address. - /// - /// - /// Gets a valid public key associated with the mailbox address that can be used for encryption. - /// - /// The public encryption key. - /// The mailbox. - /// - /// is null. - /// - /// - /// The public key for the specified could not be found. - /// - protected virtual PgpPublicKey GetPublicKey (MailboxAddress mailbox) - { - foreach (var key in EnumeratePublicKeys (mailbox)) { - if (!key.IsEncryptionKey || key.IsRevoked () || IsExpired (key)) - continue; - - return key; - } - - throw new PublicKeyNotFoundException (mailbox, "The public key could not be found."); - } - - /// - /// Get the public keys for the specified mailbox addresses. - /// - /// - /// Gets a list of valid public keys for the specified mailbox addresses that can be used for encryption. - /// - /// The encryption keys. - /// The mailboxes. - /// - /// is null. - /// - /// - /// A public key for one or more of the could not be found. - /// - public override IList GetPublicKeys (IEnumerable mailboxes) - { - if (mailboxes == null) - throw new ArgumentNullException (nameof (mailboxes)); - - var keys = new List (); - - foreach (var mailbox in mailboxes) - keys.Add (GetPublicKey (mailbox)); - - return keys; - } - - /// - /// Get the secret key for a specified key identifier. - /// - /// - /// Gets the secret key for a specified key identifier. - /// - /// The key identifier for the desired secret key. - /// The secret key. - /// - /// The secret key specified by the could not be found. - /// - protected override PgpSecretKey GetSecretKey (long keyId) - { - foreach (var key in EnumerateSecretKeys ()) { - if (key.KeyId == keyId) - return key; - } - - throw new PrivateKeyNotFoundException (keyId, "The secret key could not be found."); - } - - /// - /// Get the signing key associated with the mailbox address. - /// - /// - /// Gets the signing key associated with the mailbox address. - /// - /// The signing key. - /// The mailbox. - /// - /// is null. - /// - /// - /// A secret key for the specified could not be found. - /// - public override PgpSecretKey GetSigningKey (MailboxAddress mailbox) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - foreach (var keyring in EnumerateSecretKeyRings (mailbox)) { - foreach (PgpSecretKey key in keyring.GetSecretKeys ()) { - if (!key.IsSigningKey) - continue; - - var pubkey = key.PublicKey; - if (pubkey.IsRevoked () || IsExpired (pubkey)) - continue; - - return key; - } - } - - throw new PrivateKeyNotFoundException (mailbox, "The private key could not be found."); - } - - /// - /// Check whether or not a particular mailbox address can be used for signing. - /// - /// - /// Checks whether or not as particular mailbocx address can be used for signing. - /// - /// true if the mailbox address can be used for signing; otherwise, false. - /// The signer. - /// - /// is null. - /// - public override bool CanSign (MailboxAddress signer) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - foreach (var key in EnumerateSecretKeys (signer)) { - if (!key.IsSigningKey) - continue; - - var pubkey = key.PublicKey; - if (pubkey.IsRevoked () || IsExpired (pubkey)) - continue; - - return true; - } - - return false; - } - - /// - /// Check whether or not the cryptography context can encrypt to a particular recipient. - /// - /// - /// Checks whether or not the cryptography context can be used to encrypt to a particular recipient. - /// - /// true if the cryptography context can be used to encrypt to the designated recipient; otherwise, false. - /// The recipient's mailbox address. - /// - /// is null. - /// - public override bool CanEncrypt (MailboxAddress mailbox) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - foreach (var key in EnumeratePublicKeys (mailbox)) { - if (!key.IsEncryptionKey || key.IsRevoked () || IsExpired (key)) - continue; - - return true; - } - - return false; - } - -#if false - /// - /// Gets the private key. - /// - /// - /// Gets the private key. - /// - /// The private key. - /// The key identifier. - /// - /// The specified secret key could not be found. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - protected PgpPrivateKey GetPrivateKey (long keyId) - { - var secret = GetSecretKey (keyId); - - return GetPrivateKey (secret); - } - - PublicKeyAlgorithmTag GetPublicKeyAlgorithmTag (PublicKeyAlgorithm algorithm) - { - switch (algorithm) { - case PublicKeyAlgorithm.DiffieHellman: return PublicKeyAlgorithmTag.DiffieHellman; - case PublicKeyAlgorithm.Dsa: return PublicKeyAlgorithmTag.Dsa; - case PublicKeyAlgorithm.EdwardsCurveDsa: throw new NotSupportedException ("EDDSA is not currently supported."); - case PublicKeyAlgorithm.ElGamalEncrypt: return PublicKeyAlgorithmTag.ElGamalEncrypt; - case PublicKeyAlgorithm.ElGamalGeneral: return PublicKeyAlgorithmTag.ElGamalGeneral; - case PublicKeyAlgorithm.EllipticCurve: return PublicKeyAlgorithmTag.ECDH; - case PublicKeyAlgorithm.EllipticCurveDsa: return PublicKeyAlgorithmTag.ECDsa; - case PublicKeyAlgorithm.RsaEncrypt: return PublicKeyAlgorithmTag.RsaEncrypt; - case PublicKeyAlgorithm.RsaGeneral: return PublicKeyAlgorithmTag.RsaGeneral; - case PublicKeyAlgorithm.RsaSign: return PublicKeyAlgorithmTag.RsaSign; - default: throw new ArgumentOutOfRangeException (nameof (algorithm)); - } - } -#endif - - void AddEncryptionKeyPair (PgpKeyRingGenerator keyRingGenerator, KeyGenerationParameters parameters, PublicKeyAlgorithmTag algorithm, DateTime now, long expirationTime, int[] encryptionAlgorithms, int[] digestAlgorithms) - { - var keyPairGenerator = GeneratorUtilities.GetKeyPairGenerator ("RSA"); - - keyPairGenerator.Init (parameters); - - var keyPair = new PgpKeyPair (algorithm, keyPairGenerator.GenerateKeyPair (), now); - var subpacketGenerator = new PgpSignatureSubpacketGenerator (); - - subpacketGenerator.SetKeyFlags (false, PgpKeyFlags.CanEncryptCommunications | PgpKeyFlags.CanEncryptStorage); - subpacketGenerator.SetPreferredSymmetricAlgorithms (false, encryptionAlgorithms); - subpacketGenerator.SetPreferredHashAlgorithms (false, digestAlgorithms); - - if (expirationTime > 0) { - subpacketGenerator.SetKeyExpirationTime (false, expirationTime); - subpacketGenerator.SetSignatureExpirationTime (false, expirationTime); - } - - keyRingGenerator.AddSubKey (keyPair, subpacketGenerator.Generate (), null); - } - - PgpKeyRingGenerator CreateKeyRingGenerator (MailboxAddress mailbox, EncryptionAlgorithm algorithm, long expirationTime, string password, DateTime now, SecureRandom random) - { - var enabledEncryptionAlgorithms = EnabledEncryptionAlgorithms; - var enabledDigestAlgorithms = EnabledDigestAlgorithms; - var encryptionAlgorithms = new int[enabledEncryptionAlgorithms.Length]; - var digestAlgorithms = new int[enabledDigestAlgorithms.Length]; - - for (int i = 0; i < enabledEncryptionAlgorithms.Length; i++) - encryptionAlgorithms[i] = (int) enabledEncryptionAlgorithms[i]; - for (int i = 0; i < enabledDigestAlgorithms.Length; i++) - digestAlgorithms[i] = (int) enabledDigestAlgorithms[i]; - - var parameters = new RsaKeyGenerationParameters (BigInteger.ValueOf (0x10001), random, 2048, 12); - var signingAlgorithm = PublicKeyAlgorithmTag.RsaSign; - - var keyPairGenerator = GeneratorUtilities.GetKeyPairGenerator ("RSA"); - - keyPairGenerator.Init (parameters); - - var signingKeyPair = new PgpKeyPair (signingAlgorithm, keyPairGenerator.GenerateKeyPair (), now); - - var subpacketGenerator = new PgpSignatureSubpacketGenerator (); - subpacketGenerator.SetKeyFlags (false, PgpKeyFlags.CanSign | PgpKeyFlags.CanCertify); - subpacketGenerator.SetPreferredSymmetricAlgorithms (false, encryptionAlgorithms); - subpacketGenerator.SetPreferredHashAlgorithms (false, digestAlgorithms); - - if (expirationTime > 0) { - subpacketGenerator.SetKeyExpirationTime (false, expirationTime); - subpacketGenerator.SetSignatureExpirationTime (false, expirationTime); - } - - subpacketGenerator.SetFeature (false, Org.BouncyCastle.Bcpg.Sig.Features.FEATURE_MODIFICATION_DETECTION); - - var keyRingGenerator = new PgpKeyRingGenerator ( - PgpSignature.PositiveCertification, - signingKeyPair, - mailbox.ToString (false), - GetSymmetricKeyAlgorithm (algorithm), - CharsetUtils.UTF8.GetBytes (password), - true, - subpacketGenerator.Generate (), - null, - random); - - // Add the (optional) encryption subkey. - AddEncryptionKeyPair (keyRingGenerator, parameters, PublicKeyAlgorithmTag.RsaGeneral, now, expirationTime, encryptionAlgorithms, digestAlgorithms); - - return keyRingGenerator; - } - - /// - /// Generate a new key pair. - /// - /// - /// Generates a new RSA key pair. - /// - /// The mailbox to generate the key pair for. - /// The password to be set on the secret key. - /// The expiration date for the generated key pair. - /// The symmetric key algorithm to use. - /// The source of randomness to use when generating the key pair. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a date in the future. - /// - public void GenerateKeyPair (MailboxAddress mailbox, string password, DateTime? expirationDate = null, EncryptionAlgorithm algorithm = EncryptionAlgorithm.Aes256, SecureRandom random = null) - { - var now = DateTime.UtcNow; - long expirationTime = 0; - - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - if (expirationDate.HasValue) { - var utc = expirationDate.Value.ToUniversalTime (); - - if (utc <= now) - throw new ArgumentException ("expirationDate needs to be greater than DateTime.Now", nameof (expirationDate)); - - if ((expirationTime = Convert.ToInt64 (utc.Subtract (now).TotalSeconds)) <= 0) - throw new ArgumentException ("expirationDate needs to be greater than DateTime.Now", nameof (expirationDate)); - } - - if (random == null) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - random = new SecureRandom (new CryptoApiRandomGenerator ()); -#else - random = new SecureRandom (); -#endif - } - - var generator = CreateKeyRingGenerator (mailbox, algorithm, expirationTime, password, now, random); - - Import (generator.GenerateSecretKeyRing ()); - Import (generator.GeneratePublicKeyRing ()); - } - - /// - /// Sign a public key. - /// - /// - /// Signs a public key using the specified secret key. - /// Most OpenPGP implementations use - /// to make their "key signatures". Some implementations are known to use the other - /// certification types, but few differentiate between them. - /// - /// The secret key to use for signing. - /// The public key to sign. - /// The digest algorithm. - /// The certification to give the signed key. - /// - /// is null. - /// -or- - /// is null. - /// - public void SignKey (PgpSecretKey secretKey, PgpPublicKey publicKey, DigestAlgorithm digestAlgo = DigestAlgorithm.Sha1, OpenPgpKeyCertification certification = OpenPgpKeyCertification.GenericCertification) - { - if (secretKey == null) - throw new ArgumentNullException (nameof (secretKey)); - - if (publicKey == null) - throw new ArgumentNullException (nameof (publicKey)); - - var privateKey = GetPrivateKey (secretKey); - var signatureGenerator = new PgpSignatureGenerator (secretKey.PublicKey.Algorithm, GetHashAlgorithm (digestAlgo)); - - signatureGenerator.InitSign ((int) certification, privateKey); - signatureGenerator.GenerateOnePassVersion (false); - - var subpacketGenerator = new PgpSignatureSubpacketGenerator (); - var subpacketVector = subpacketGenerator.Generate (); - - signatureGenerator.SetHashedSubpackets (subpacketVector); - - var signedKey = PgpPublicKey.AddCertification (publicKey, signatureGenerator.Generate ()); - PgpPublicKeyRing keyring = null; - - foreach (var ring in EnumeratePublicKeyRings ()) { - foreach (PgpPublicKey key in ring.GetPublicKeys ()) { - if (key.KeyId == publicKey.KeyId) { - PublicKeyRingBundle = PgpPublicKeyRingBundle.RemovePublicKeyRing (PublicKeyRingBundle, ring); - keyring = PgpPublicKeyRing.InsertPublicKey (ring, signedKey); - break; - } - } - } - - if (keyring == null) - keyring = new PgpPublicKeyRing (signedKey.GetEncoded ()); - - Import (keyring); - } - - /// - /// Saves the public key-ring bundle. - /// - /// - /// Atomically saves the public key-ring bundle to the path specified by . - /// Called by if any public keys were successfully imported. - /// - /// - /// An error occured while saving the public key-ring bundle. - /// - protected void SavePublicKeyRingBundle () - { - var filename = Path.GetFileName (PublicKeyRingPath) + "~"; - var dirname = Path.GetDirectoryName (PublicKeyRingPath); - var tmp = Path.Combine (dirname, "." + filename); - var bak = Path.Combine (dirname, filename); - - Directory.CreateDirectory (dirname); - - using (var file = File.Open (tmp, FileMode.Create, FileAccess.Write)) { - PublicKeyRingBundle.Encode (file); - file.Flush (); - } - - if (File.Exists (PublicKeyRingPath)) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - File.Replace (tmp, PublicKeyRingPath, bak); -#else - if (File.Exists (bak)) - File.Delete (bak); - File.Move (PublicKeyRingPath, bak); - File.Move (tmp, PublicKeyRingPath); -#endif - } else { - File.Move (tmp, PublicKeyRingPath); - } - } - - /// - /// Saves the secret key-ring bundle. - /// - /// - /// Atomically saves the secret key-ring bundle to the path specified by . - /// Called by if any secret keys were successfully imported. - /// - /// - /// An error occured while saving the secret key-ring bundle. - /// - protected void SaveSecretKeyRingBundle () - { - var filename = Path.GetFileName (SecretKeyRingPath) + "~"; - var dirname = Path.GetDirectoryName (SecretKeyRingPath); - var tmp = Path.Combine (dirname, "." + filename); - var bak = Path.Combine (dirname, filename); - - Directory.CreateDirectory (dirname); - - using (var file = File.Open (tmp, FileMode.Create, FileAccess.Write)) { - SecretKeyRingBundle.Encode (file); - file.Flush (); - } - - if (File.Exists (SecretKeyRingPath)) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - File.Replace (tmp, SecretKeyRingPath, bak); -#else - if (File.Exists (bak)) - File.Delete (bak); - File.Move (SecretKeyRingPath, bak); - File.Move (tmp, SecretKeyRingPath); -#endif - } else { - File.Move (tmp, SecretKeyRingPath); - } - } - - /// - /// Imports a public pgp keyring. - /// - /// - /// Imports a public pgp keyring. - /// - /// The pgp keyring. - /// - /// is null. - /// - public virtual void Import (PgpPublicKeyRing keyring) - { - if (keyring == null) - throw new ArgumentNullException (nameof (keyring)); - - PublicKeyRingBundle = PgpPublicKeyRingBundle.AddPublicKeyRing (PublicKeyRingBundle, keyring); - SavePublicKeyRingBundle (); - } - - /// - /// Imports a public pgp keyring bundle. - /// - /// - /// Imports a public pgp keyring bundle. - /// - /// The pgp keyring bundle. - /// - /// is null. - /// - public override void Import (PgpPublicKeyRingBundle bundle) - { - if (bundle == null) - throw new ArgumentNullException (nameof (bundle)); - - int publicKeysAdded = 0; - - foreach (PgpPublicKeyRing pubring in bundle.GetKeyRings ()) { - PublicKeyRingBundle = PgpPublicKeyRingBundle.AddPublicKeyRing (PublicKeyRingBundle, pubring); - publicKeysAdded++; - } - - if (publicKeysAdded > 0) - SavePublicKeyRingBundle (); - } - - /// - /// Imports public pgp keys from the specified stream. - /// - /// - /// Imports public pgp keys from the specified stream. - /// - /// The raw key data. - /// - /// is null. - /// - /// - /// An error occurred while parsing the raw key-ring data - /// -or- - /// An error occured while saving the public key-ring bundle. - /// - public override void Import (Stream stream) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - using (var armored = new ArmoredInputStream (stream)) - Import (new PgpPublicKeyRingBundle (armored)); - } - - /// - /// Imports a secret pgp keyring. - /// - /// - /// Imports a secret pgp keyring. - /// - /// The pgp keyring. - /// - /// is null. - /// - public virtual void Import (PgpSecretKeyRing keyring) - { - if (keyring == null) - throw new ArgumentNullException (nameof (keyring)); - - SecretKeyRingBundle = PgpSecretKeyRingBundle.AddSecretKeyRing (SecretKeyRingBundle, keyring); - SaveSecretKeyRingBundle (); - } - - /// - /// Imports a secret pgp keyring bundle. - /// - /// - /// Imports a secret pgp keyring bundle. - /// - /// The pgp keyring bundle. - /// - /// is null. - /// - public virtual void Import (PgpSecretKeyRingBundle bundle) - { - if (bundle == null) - throw new ArgumentNullException (nameof (bundle)); - - int secretKeysAdded = 0; - - foreach (PgpSecretKeyRing secring in bundle.GetKeyRings ()) { - SecretKeyRingBundle = PgpSecretKeyRingBundle.AddSecretKeyRing (SecretKeyRingBundle, secring); - secretKeysAdded++; - } - - if (secretKeysAdded > 0) - SaveSecretKeyRingBundle (); - } - - /// - /// Exports the public keys for the specified mailboxes. - /// - /// - /// Exports the public keys for the specified mailboxes. - /// - /// A new instance containing the exported public keys. - /// The mailboxes associated with the public keys to export. - /// - /// is null. - /// - /// - /// was empty. - /// - public override MimePart Export (IEnumerable mailboxes) - { - if (mailboxes == null) - throw new ArgumentNullException (nameof (mailboxes)); - - var keyrings = new List (); - foreach (var mailbox in mailboxes) - keyrings.AddRange (EnumeratePublicKeyRings (mailbox)); - - var bundle = new PgpPublicKeyRingBundle (keyrings); - - return Export (bundle); - } - - /// - /// Exports the specified public keys. - /// - /// - /// Exports the specified public keys. - /// - /// A new instance containing the exported public keys. - /// The public keys to export. - /// - /// is null. - /// - public MimePart Export (IEnumerable keys) - { - if (keys == null) - throw new ArgumentNullException (nameof (keys)); - - var keyrings = keys.Select (key => new PgpPublicKeyRing (key.GetEncoded ())); - var bundle = new PgpPublicKeyRingBundle (keyrings); - - return Export (bundle); - } - - /// - /// Export the specified public keys. - /// - /// - /// Exports the specified public keys. - /// - /// A new instance containing the exported public keys. - /// The public keys to export. - /// - /// is null. - /// - public MimePart Export (PgpPublicKeyRingBundle keys) - { - if (keys == null) - throw new ArgumentNullException (nameof (keys)); - - var content = new MemoryBlockStream (); - - Export (keys, content, true); - - content.Position = 0; - - return new MimePart ("application", "pgp-keys") { - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment), - Content = new MimeContent (content) - }; - } - - /// - /// Export the public keyrings for the specified mailboxes. - /// - /// - /// Exports the public keyrings for the specified mailboxes. - /// - /// The mailboxes. - /// The output stream. - /// true if the output should be armored; otherwise, false. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An I/O error occurred. - /// - public void Export (IEnumerable mailboxes, Stream stream, bool armor) - { - if (mailboxes == null) - throw new ArgumentNullException (nameof (mailboxes)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var keyrings = new List (); - foreach (var mailbox in mailboxes) - keyrings.AddRange (EnumeratePublicKeyRings (mailbox)); - - var bundle = new PgpPublicKeyRingBundle (keyrings); - - Export (bundle, stream, armor); - } - - /// - /// Export the specified public keys. - /// - /// - /// Exports the specified public keys. - /// - /// A new instance containing the exported public keys. - /// The public keys to export. - /// The output stream. - /// true if the output should be armored; otherwise, false. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An I/O error occurred. - /// - public void Export (IEnumerable keys, Stream stream, bool armor) - { - if (keys == null) - throw new ArgumentNullException (nameof (keys)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var keyrings = keys.Select (key => new PgpPublicKeyRing (key.GetEncoded ())); - var bundle = new PgpPublicKeyRingBundle (keyrings); - - Export (bundle, stream, armor); - } - - /// - /// Export the public keyring bundle. - /// - /// - /// Exports the public keyring bundle. - /// - /// The public keyring bundle to export. - /// The output stream. - /// true if the output should be armored; otherwise, false. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An I/O error occurred. - /// - public void Export (PgpPublicKeyRingBundle keys, Stream stream, bool armor) - { - if (keys == null) - throw new ArgumentNullException (nameof (keys)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (armor) { - using (var armored = new ArmoredOutputStream (stream)) { - armored.SetHeader ("Version", null); - - keys.Encode (armored); - armored.Flush (); - } - } else { - keys.Encode (stream); - } - } - - /// - /// Delete a public pgp keyring. - /// - /// - /// Deletes a public pgp keyring. - /// - /// The pgp keyring. - /// - /// is null. - /// - public virtual void Delete (PgpPublicKeyRing keyring) - { - if (keyring == null) - throw new ArgumentNullException (nameof (keyring)); - - PublicKeyRingBundle = PgpPublicKeyRingBundle.RemovePublicKeyRing (PublicKeyRingBundle, keyring); - SavePublicKeyRingBundle (); - } - - /// - /// Delete a secret pgp keyring. - /// - /// - /// Deletes a secret pgp keyring. - /// - /// The pgp keyring. - /// - /// is null. - /// - public virtual void Delete (PgpSecretKeyRing keyring) - { - if (keyring == null) - throw new ArgumentNullException (nameof (keyring)); - - SecretKeyRingBundle = PgpSecretKeyRingBundle.RemoveSecretKeyRing (SecretKeyRingBundle, keyring); - SaveSecretKeyRingBundle (); - } - } -} diff --git a/src/MimeKit/Cryptography/OpenPgpContextBase.cs b/src/MimeKit/Cryptography/OpenPgpContextBase.cs deleted file mode 100644 index 5a9b8a8..0000000 --- a/src/MimeKit/Cryptography/OpenPgpContextBase.cs +++ /dev/null @@ -1,1887 +0,0 @@ -// -// OpenPgpContext.cs -// -// Authors: Jeffrey Stedfast -// Thomas Hansen -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Net.Http; -using System.Threading; -using System.Diagnostics; -using System.Threading.Tasks; -using System.Collections.Generic; - -using Org.BouncyCastle.Bcpg; -using Org.BouncyCastle.Bcpg.OpenPgp; - -using MimeKit.IO; - -namespace MimeKit.Cryptography { - /// - /// An abstract OpenPGP cryptography context which can be used for PGP/MIME. - /// - /// - /// Generally speaking, applications should not use a - /// directly, but rather via higher level APIs such as - /// and . - /// - public abstract class OpenPgpContextBase : CryptographyContext - { - static readonly string[] ProtocolSubtypes = { "pgp-signature", "pgp-encrypted", "pgp-keys", "x-pgp-signature", "x-pgp-encrypted", "x-pgp-keys" }; - const string BeginPublicKeyBlock = "-----BEGIN PGP PUBLIC KEY BLOCK-----"; - const string EndPublicKeyBlock = "-----END PGP PUBLIC KEY BLOCK-----"; - - static readonly EncryptionAlgorithm[] DefaultEncryptionAlgorithmRank = { - EncryptionAlgorithm.Idea, - EncryptionAlgorithm.TripleDes, - EncryptionAlgorithm.Cast5, - EncryptionAlgorithm.Blowfish, - EncryptionAlgorithm.Aes128, - EncryptionAlgorithm.Aes192, - EncryptionAlgorithm.Aes256, - EncryptionAlgorithm.Twofish, - EncryptionAlgorithm.Camellia128, - EncryptionAlgorithm.Camellia192, - EncryptionAlgorithm.Camellia256 - }; - - static readonly DigestAlgorithm[] DefaultDigestAlgorithmRank = { - DigestAlgorithm.Sha1, - DigestAlgorithm.RipeMD160, - DigestAlgorithm.Sha256, - DigestAlgorithm.Sha384, - DigestAlgorithm.Sha512, - DigestAlgorithm.Sha224 - }; - - EncryptionAlgorithm defaultAlgorithm; - HttpClient client; - Uri keyServer; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - protected OpenPgpContextBase () - { - EncryptionAlgorithmRank = DefaultEncryptionAlgorithmRank; - DigestAlgorithmRank = DefaultDigestAlgorithmRank; - - foreach (var algorithm in EncryptionAlgorithmRank) - Enable (algorithm); - - foreach (var algorithm in DigestAlgorithmRank) - Enable (algorithm); - - defaultAlgorithm = EncryptionAlgorithm.Cast5; - - client = new HttpClient (); - } - - /// - /// Get the password for a secret key. - /// - /// - /// Gets the password for a secret key. - /// - /// The password for the secret key. - /// The secret key. - /// - /// The user chose to cancel the password request. - /// - protected abstract string GetPasswordForKey (PgpSecretKey key); // FIXME: rename this to GetPassword() in the future - - /// - /// Get the public keyring that contains the specified key. - /// - /// - /// Gets the public keyring that contains the specified key. - /// Implementations should first try to obtain the keyring stored (or cached) locally. - /// Failing that, if is enabled, they should use - /// to attempt to - /// retrieve the keyring from the configured . - /// - /// The public key identifier. - /// The cancellation token. - /// The public keyring that contains the specified key or null if the keyring could not be found. - /// - /// The operation was cancelled. - /// - protected abstract PgpPublicKeyRing GetPublicKeyRing (long keyId, CancellationToken cancellationToken); - - /// - /// Get the public keyring that contains the specified key asynchronously. - /// - /// - /// Gets the public keyring that contains the specified key. - /// Implementations should first try to obtain the keyring stored (or cached) locally. - /// Failing that, if is enabled, they should use - /// to attempt to - /// retrieve the keyring from the configured . - /// - /// The public key identifier. - /// The cancellation token. - /// The public keyring that contains the specified key or null if the keyring could not be found. - /// - /// The operation was cancelled. - /// - protected abstract Task GetPublicKeyRingAsync (long keyId, CancellationToken cancellationToken); - - /// - /// Get the secret key for a specified key identifier. - /// - /// - /// Gets the secret key for a specified key identifier. - /// - /// The key identifier for the desired secret key. - /// The secret key. - /// - /// The secret key specified by the could not be found. - /// - protected abstract PgpSecretKey GetSecretKey (long keyId); - - /// - /// Get the public keys for the specified mailbox addresses. - /// - /// - /// Gets a list of valid public keys for the specified mailbox addresses that can be used for encryption. - /// - /// The encryption keys. - /// The mailboxes. - /// - /// is null. - /// - /// - /// A public key for one or more of the could not be found. - /// - public abstract IList GetPublicKeys (IEnumerable mailboxes); - - /// - /// Get the signing key associated with the mailbox address. - /// - /// - /// Gets the signing key associated with the mailbox address. - /// - /// The signing key. - /// The mailbox. - /// - /// is null. - /// - /// - /// A secret key for the specified could not be found. - /// - public abstract PgpSecretKey GetSigningKey (MailboxAddress mailbox); - - /// - /// Get or set the default encryption algorithm. - /// - /// - /// Gets or sets the default encryption algorithm. - /// - /// The encryption algorithm. - /// - /// The specified encryption algorithm is not supported. - /// - public EncryptionAlgorithm DefaultEncryptionAlgorithm { - get { return defaultAlgorithm; } - set { - GetSymmetricKeyAlgorithm (value); - defaultAlgorithm = value; - } - } - - bool IsValidKeyServer { - get { - if (keyServer == null) - return false; - - switch (keyServer.Scheme.ToLowerInvariant ()) { - case "https": case "http": case "hkp": return true; - default: return false; - } - } - } - - /// - /// Get or set the key server to use when automatically retrieving keys. - /// - /// - /// Gets or sets the key server to use when verifying keys that are - /// not already in the public keychain. - /// Only HTTP and HKP protocols are supported. - /// - /// The key server. - /// - /// is not an absolute URI. - /// - public Uri KeyServer { - get { return keyServer; } - set { - if (value != null && !value.IsAbsoluteUri) - throw new ArgumentException ("The key server URI must be absolute.", nameof (value)); - - keyServer = value; - } - } - - /// - /// Get or set whether unknown PGP keys should automtically be retrieved. - /// - /// - /// Gets or sets whether or not the should automatically - /// fetch keys as needed from the keyserver when verifying signatures. - /// Requires a valid to be set. - /// - /// true if unknown PGP keys should automatically be retrieved; otherwise, false. - public bool AutoKeyRetrieve { - get; set; - } - - /// - /// Get the signature protocol. - /// - /// - /// The signature protocol is used by - /// in order to determine what the protocol parameter of the Content-Type - /// header should be. - /// - /// The signature protocol. - public override string SignatureProtocol { - get { return "application/pgp-signature"; } - } - - /// - /// Get the encryption protocol. - /// - /// - /// The encryption protocol is used by - /// in order to determine what the protocol parameter of the Content-Type - /// header should be. - /// - /// The encryption protocol. - public override string EncryptionProtocol { - get { return "application/pgp-encrypted"; } - } - - /// - /// Get the key exchange protocol. - /// - /// - /// Gets the key exchange protocol. - /// - /// The key exchange protocol. - public override string KeyExchangeProtocol { - get { return "application/pgp-keys"; } - } - - /// - /// Check whether or not the specified protocol is supported. - /// - /// - /// Used in order to make sure that the protocol parameter value specified in either a multipart/signed - /// or multipart/encrypted part is supported by the supplied cryptography context. - /// - /// true if the protocol is supported; otherwise false - /// The protocol. - /// - /// is null. - /// - public override bool Supports (string protocol) - { - if (protocol == null) - throw new ArgumentNullException (nameof (protocol)); - - if (!protocol.StartsWith ("application/", StringComparison.OrdinalIgnoreCase)) - return false; - - int startIndex = "application/".Length; - int subtypeLength = protocol.Length - startIndex; - - for (int i = 0; i < ProtocolSubtypes.Length; i++) { - if (subtypeLength != ProtocolSubtypes[i].Length) - continue; - - if (string.Compare (protocol, startIndex, ProtocolSubtypes[i], 0, subtypeLength, StringComparison.OrdinalIgnoreCase) == 0) - return true; - } - - return false; - } - - /// - /// Get the string name of the digest algorithm for use with the micalg parameter of a multipart/signed part. - /// - /// - /// Maps the to the appropriate string identifier - /// as used by the micalg parameter value of a multipart/signed Content-Type - /// header. For example: - /// - /// AlgorithmName - /// pgp-md5 - /// pgp-sha1 - /// pgp-ripemd160 - /// pgp-md2 - /// pgp-tiger192 - /// pgp-haval-5-160 - /// pgp-sha256 - /// pgp-sha384 - /// pgp-sha512 - /// pgp-sha224 - /// - /// - /// The micalg value. - /// The digest algorithm. - /// - /// is out of range. - /// - public override string GetDigestAlgorithmName (DigestAlgorithm micalg) - { - switch (micalg) { - case DigestAlgorithm.MD5: return "pgp-md5"; - case DigestAlgorithm.Sha1: return "pgp-sha1"; - case DigestAlgorithm.RipeMD160: return "pgp-ripemd160"; - case DigestAlgorithm.MD2: return "pgp-md2"; - case DigestAlgorithm.Tiger192: return "pgp-tiger192"; - case DigestAlgorithm.Haval5160: return "pgp-haval-5-160"; - case DigestAlgorithm.Sha256: return "pgp-sha256"; - case DigestAlgorithm.Sha384: return "pgp-sha384"; - case DigestAlgorithm.Sha512: return "pgp-sha512"; - case DigestAlgorithm.Sha224: return "pgp-sha224"; - case DigestAlgorithm.MD4: return "pgp-md4"; - default: throw new ArgumentOutOfRangeException (nameof (micalg)); - } - } - - /// - /// Get the digest algorithm from the micalg parameter value in a multipart/signed part. - /// - /// - /// Maps the micalg parameter value string back to the appropriate . - /// - /// The digest algorithm. - /// The micalg parameter value. - /// - /// is null. - /// - public override DigestAlgorithm GetDigestAlgorithm (string micalg) - { - if (micalg == null) - throw new ArgumentNullException (nameof (micalg)); - - switch (micalg.ToLowerInvariant ()) { - case "pgp-md5": return DigestAlgorithm.MD5; - case "pgp-sha1": return DigestAlgorithm.Sha1; - case "pgp-ripemd160": return DigestAlgorithm.RipeMD160; - case "pgp-md2": return DigestAlgorithm.MD2; - case "pgp-tiger192": return DigestAlgorithm.Tiger192; - case "pgp-haval-5-160": return DigestAlgorithm.Haval5160; - case "pgp-sha256": return DigestAlgorithm.Sha256; - case "pgp-sha384": return DigestAlgorithm.Sha384; - case "pgp-sha512": return DigestAlgorithm.Sha512; - case "pgp-sha224": return DigestAlgorithm.Sha224; - case "pgp-md4": return DigestAlgorithm.MD4; - default: return DigestAlgorithm.None; - } - } - - /// - /// Hex encode an array of bytes. - /// - /// - /// This method is used to hex-encode the PGP key fingerprints. - /// - /// The data to encode. - /// A string representing the hex-encoded data. - static string HexEncode (byte[] data) - { - var fingerprint = new StringBuilder (); - - for (int i = 0; i < data.Length; i++) - fingerprint.Append (data[i].ToString ("x2")); - - return fingerprint.ToString (); - } - - /// - /// Check that a public key is a match for the specified mailbox. - /// - /// - /// Checks that the public key is a match for the specified mailbox. - /// If the is a with a non-empty - /// , then the fingerprint is used to match the key's - /// fingerprint. Otherwise, the email address(es) contained within the key's user identifier strings - /// are compared to the mailbox address. - /// - /// The public key. - /// The mailbox address. - /// true if the key is a match for the specified mailbox; otherwise, false. - /// - /// is null. - /// -or- - /// is null. - /// - protected static bool IsMatch (PgpPublicKey key, MailboxAddress mailbox) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - if (mailbox is SecureMailboxAddress secure && !string.IsNullOrEmpty (secure.Fingerprint)) { - if (secure.Fingerprint.Length > 16) { - var fingerprint = HexEncode (key.GetFingerprint ()); - - return secure.Fingerprint.Equals (fingerprint, StringComparison.OrdinalIgnoreCase); - } - - var id = ((int) key.KeyId).ToString ("X2"); - - return secure.Fingerprint.EndsWith (id, StringComparison.OrdinalIgnoreCase); - } - - foreach (string userId in key.GetUserIds ()) { - if (!MailboxAddress.TryParse (userId, out var email)) - continue; - - if (mailbox.Address.Equals (email.Address, StringComparison.OrdinalIgnoreCase)) - return true; - } - - return false; - } - - /// - /// Check that a secret key is a match for the specified mailbox. - /// - /// - /// Checks that the secret key is a match for the specified mailbox. - /// If the is a with a non-empty - /// , then the fingerprint is used to match the key's - /// fingerprint. Otherwise, the email address(es) contained within the key's user identifier strings - /// are compared to the mailbox address. - /// - /// The secret key. - /// The mailbox address. - /// true if the key is a match for the specified mailbox; otherwise, false. - /// - /// is null. - /// -or- - /// is null. - /// - protected static bool IsMatch (PgpSecretKey key, MailboxAddress mailbox) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - if (mailbox is SecureMailboxAddress secure && !string.IsNullOrEmpty (secure.Fingerprint)) { - if (secure.Fingerprint.Length > 16) { - var fingerprint = HexEncode (key.PublicKey.GetFingerprint ()); - - return secure.Fingerprint.Equals (fingerprint, StringComparison.OrdinalIgnoreCase); - } - - var id = ((int) key.KeyId).ToString ("X2"); - - return secure.Fingerprint.EndsWith (id, StringComparison.OrdinalIgnoreCase); - } - - foreach (string userId in key.UserIds) { - if (!MailboxAddress.TryParse (userId, out var email)) - continue; - - if (mailbox.Address.Equals (email.Address, StringComparison.OrdinalIgnoreCase)) - return true; - } - - return false; - } - - /// - /// Check if a public key is expired. - /// - /// - /// Checks if a public key is expired. - /// - /// The public key. - /// true if the public key is expired; otherwise, false. - /// - /// is null. - /// - protected static bool IsExpired (PgpPublicKey key) - { - if (key == null) - throw new ArgumentNullException (nameof (key)); - - long seconds = key.GetValidSeconds (); - - if (seconds != 0) { - var expires = key.CreationTime.AddSeconds ((double) seconds); - if (expires <= DateTime.Now) - return true; - } - - return false; - } - - /// - /// Retrieves the public keyring, using the preferred key server, automatically importing it afterwards. - /// - /// The identifier of the key to be retrieved. - /// true if this operation should be done asynchronously; otherweise, false. - /// The cancellation token. - /// The public key ring. - async Task RetrievePublicKeyRingAsync (long keyId, bool doAsync, CancellationToken cancellationToken) - { - if (!IsValidKeyServer) - return null; - - var scheme = keyServer.Scheme.ToLowerInvariant (); - var uri = new UriBuilder (); - - uri.Scheme = scheme == "hkp" ? "http" : scheme; - uri.Host = keyServer.Host; - - if (keyServer.IsDefaultPort) { - if (scheme == "hkp") - uri.Port = 11371; - } else { - uri.Port = keyServer.Port; - } - - uri.Path = "/pks/lookup"; - uri.Query = string.Format ("op=get&search=0x{0:X}", keyId); - - using (var stream = new MemoryBlockStream ()) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (new OpenPgpBlockFilter (BeginPublicKeyBlock, EndPublicKeyBlock)); - - if (doAsync) { - using (var response = await client.GetAsync (uri.ToString (), cancellationToken).ConfigureAwait (false)) - await response.Content.CopyToAsync (filtered).ConfigureAwait (false); - } else { -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - var request = (HttpWebRequest) WebRequest.Create (uri.ToString ()); - using (var response = request.GetResponse ()) { - var content = response.GetResponseStream (); - content.CopyTo (filtered, 4096); - } -#else - using (var response = client.GetAsync (uri.ToString (), cancellationToken).GetAwaiter ().GetResult ()) - response.Content.CopyToAsync (filtered).GetAwaiter ().GetResult (); -#endif - } - - filtered.Flush (); - } - - stream.Position = 0; - - using (var armored = new ArmoredInputStream (stream, true)) { - var bundle = new PgpPublicKeyRingBundle (armored); - - Import (bundle); - - return bundle.GetPublicKeyRing (keyId); - } - } - } - - /// - /// Retrieve the public keyring using the configured key server. - /// - /// - /// Retrieves the public keyring specified by the from the key server - /// set on the property. If the keyring is successfully retrieved, it will - /// be imported via . - /// This method should be called by - /// when the keyring is not available locally. - /// - /// The identifier of the public key to be retrieved. - /// The cancellation token. - /// The public key ring. - protected PgpPublicKeyRing RetrievePublicKeyRing (long keyId, CancellationToken cancellationToken) - { - return RetrievePublicKeyRingAsync (keyId, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously retrieve the public keyring using the configured key server. - /// - /// - /// Retrieves the public keyring specified by the from the key server - /// set on the property. If the keyring is successfully retrieved, it will - /// be imported via . - /// This method should be called by - /// when the keyring is not available locally. - /// - /// The identifier of the public key to be retrieved. - /// The cancellation token. - /// The public key ring. - protected Task RetrievePublicKeyRingAsync (long keyId, CancellationToken cancellationToken) - { - return RetrievePublicKeyRingAsync (keyId, true, cancellationToken); - } - - /// - /// Gets the private key from the specified secret key. - /// - /// - /// Gets the private key from the specified secret key. - /// - /// The private key. - /// The secret key. - /// - /// is null. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - protected PgpPrivateKey GetPrivateKey (PgpSecretKey key) - { - int attempts = 0; - string password; - - if (key == null) - throw new ArgumentNullException (nameof (key)); - - do { - if ((password = GetPasswordForKey (key)) == null) - throw new OperationCanceledException (); - - try { - var privateKey = key.ExtractPrivateKey (password.ToCharArray ()); - - // Note: the private key will be null if the private key is empty. - if (privateKey == null) - break; - - return privateKey; - } catch (Exception ex) { -#if DEBUG - Debug.WriteLine (string.Format ("Failed to extract secret key: {0}", ex)); -#endif - } - - attempts++; - } while (attempts < 3); - - throw new UnauthorizedAccessException (); - } - - /// - /// Gets the equivalent for the - /// specified . - /// - /// - /// Maps a to the equivalent . - /// - /// The hash algorithm. - /// The digest algorithm. - /// - /// is out of range. - /// - /// - /// is not a supported digest algorithm. - /// - public static HashAlgorithmTag GetHashAlgorithm (DigestAlgorithm digestAlgo) - { - switch (digestAlgo) { - case DigestAlgorithm.MD5: return HashAlgorithmTag.MD5; - case DigestAlgorithm.Sha1: return HashAlgorithmTag.Sha1; - case DigestAlgorithm.RipeMD160: return HashAlgorithmTag.RipeMD160; - case DigestAlgorithm.DoubleSha: throw new NotSupportedException ("The Double SHA digest algorithm is not supported."); - case DigestAlgorithm.MD2: return HashAlgorithmTag.MD2; - case DigestAlgorithm.Tiger192: throw new NotSupportedException ("The Tiger-192 digest algorithm is not supported."); - case DigestAlgorithm.Haval5160: throw new NotSupportedException ("The HAVAL 5 160 digest algorithm is not supported."); - case DigestAlgorithm.Sha256: return HashAlgorithmTag.Sha256; - case DigestAlgorithm.Sha384: return HashAlgorithmTag.Sha384; - case DigestAlgorithm.Sha512: return HashAlgorithmTag.Sha512; - case DigestAlgorithm.Sha224: return HashAlgorithmTag.Sha224; - case DigestAlgorithm.MD4: throw new NotSupportedException ("The MD4 digest algorithm is not supported."); - default: throw new ArgumentOutOfRangeException (nameof (digestAlgo)); - } - } - - /// - /// Cryptographically signs the content. - /// - /// - /// Cryptographically signs the content using the specified signer and digest algorithm. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - /// - /// A signing key could not be found for . - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public override MimePart Sign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var key = GetSigningKey (signer); - - return Sign (key, digestAlgo, content); - } - - /// - /// Cryptographically signs the content. - /// - /// - /// Cryptographically signs the content using the specified signer and digest algorithm. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public ApplicationPgpSignature Sign (PgpSecretKey signer, DigestAlgorithm digestAlgo, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (!signer.IsSigningKey) - throw new ArgumentException ("The specified secret key cannot be used for signing.", nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var hashAlgorithm = GetHashAlgorithm (digestAlgo); - var memory = new MemoryBlockStream (); - - using (var armored = new ArmoredOutputStream (memory)) { - armored.SetHeader ("Version", null); - - var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); - using (var compressed = compresser.Open (armored)) { - var signatureGenerator = new PgpSignatureGenerator (signer.PublicKey.Algorithm, hashAlgorithm); - var buf = new byte[4096]; - int nread; - - signatureGenerator.InitSign (PgpSignature.CanonicalTextDocument, GetPrivateKey (signer)); - - while ((nread = content.Read (buf, 0, buf.Length)) > 0) - signatureGenerator.Update (buf, 0, nread); - - var signature = signatureGenerator.Generate (); - - signature.Encode (compressed); - compressed.Flush (); - } - - armored.Flush (); - } - - memory.Position = 0; - - return new ApplicationPgpSignature (memory); - } - - /// - /// Gets the equivalent for the specified - /// . - /// - /// - /// Gets the equivalent for the specified - /// . - /// - /// The digest algorithm. - /// The hash algorithm. - /// - /// is out of range. - /// - /// - /// does not have an equivalent value. - /// - public static DigestAlgorithm GetDigestAlgorithm (HashAlgorithmTag hashAlgorithm) - { - switch (hashAlgorithm) { - case HashAlgorithmTag.MD5: return DigestAlgorithm.MD5; - case HashAlgorithmTag.Sha1: return DigestAlgorithm.Sha1; - case HashAlgorithmTag.RipeMD160: return DigestAlgorithm.RipeMD160; - case HashAlgorithmTag.DoubleSha: return DigestAlgorithm.DoubleSha; - case HashAlgorithmTag.MD2: return DigestAlgorithm.MD2; - case HashAlgorithmTag.Tiger192: return DigestAlgorithm.Tiger192; - case HashAlgorithmTag.Haval5pass160: return DigestAlgorithm.Haval5160; - case HashAlgorithmTag.Sha256: return DigestAlgorithm.Sha256; - case HashAlgorithmTag.Sha384: return DigestAlgorithm.Sha384; - case HashAlgorithmTag.Sha512: return DigestAlgorithm.Sha512; - case HashAlgorithmTag.Sha224: return DigestAlgorithm.Sha224; - default: throw new ArgumentOutOfRangeException (nameof (hashAlgorithm)); - } - } - - /// - /// Gets the equivalent for the specified - /// . - /// - /// - /// Gets the equivalent for the specified - /// . - /// - /// The public-key algorithm. - /// The public-key algorithm. - /// - /// is out of range. - /// - /// - /// does not have an equivalent value. - /// - public static PublicKeyAlgorithm GetPublicKeyAlgorithm (PublicKeyAlgorithmTag algorithm) - { - switch (algorithm) { - case PublicKeyAlgorithmTag.RsaGeneral: return PublicKeyAlgorithm.RsaGeneral; - case PublicKeyAlgorithmTag.RsaEncrypt: return PublicKeyAlgorithm.RsaEncrypt; - case PublicKeyAlgorithmTag.RsaSign: return PublicKeyAlgorithm.RsaSign; - case PublicKeyAlgorithmTag.ElGamalGeneral: return PublicKeyAlgorithm.ElGamalGeneral; - case PublicKeyAlgorithmTag.ElGamalEncrypt: return PublicKeyAlgorithm.ElGamalEncrypt; - case PublicKeyAlgorithmTag.Dsa: return PublicKeyAlgorithm.Dsa; - case PublicKeyAlgorithmTag.ECDH: return PublicKeyAlgorithm.EllipticCurve; - case PublicKeyAlgorithmTag.ECDsa: return PublicKeyAlgorithm.EllipticCurveDsa; - case PublicKeyAlgorithmTag.DiffieHellman: return PublicKeyAlgorithm.DiffieHellman; - default: throw new ArgumentOutOfRangeException (nameof (algorithm)); - } - } - - bool TryGetPublicKey (PgpPublicKeyRing keyring, long keyId, out PgpPublicKey pubkey) - { - if (keyring != null) { - foreach (PgpPublicKey key in keyring.GetPublicKeys ()) { - if (key.KeyId == keyId) { - pubkey = key; - return true; - } - } - } - - pubkey = null; - - return false; - } - - async Task GetDigitalSignaturesAsync (PgpSignatureList signatureList, Stream content, bool doAsync, CancellationToken cancellationToken) - { - var signatures = new List (); - var buf = new byte[4096]; - int nread; - - for (int i = 0; i < signatureList.Count; i++) { - long keyId = signatureList[i].KeyId; - PgpPublicKeyRing keyring; - - if (doAsync) - keyring = await GetPublicKeyRingAsync (keyId, cancellationToken).ConfigureAwait (false); - else - keyring = GetPublicKeyRing (keyId, cancellationToken); - - TryGetPublicKey (keyring, keyId, out var key); - - var signature = new OpenPgpDigitalSignature (keyring, key, signatureList[i]) { - PublicKeyAlgorithm = GetPublicKeyAlgorithm (signatureList[i].KeyAlgorithm), - DigestAlgorithm = GetDigestAlgorithm (signatureList[i].HashAlgorithm), - CreationDate = signatureList[i].CreationTime, - }; - - if (key != null) - signatureList[i].InitVerify (key); - - signatures.Add (signature); - } - - while ((nread = content.Read (buf, 0, buf.Length)) > 0) { - for (int i = 0; i < signatures.Count; i++) { - if (signatures[i].SignerCertificate != null) { - var pgp = (OpenPgpDigitalSignature) signatures[i]; - pgp.Signature.Update (buf, 0, nread); - } - } - } - - return new DigitalSignatureCollection (signatures); - } - - Task VerifyAsync (Stream content, Stream signatureData, bool doAsync, CancellationToken cancellationToken) - { - if (content == null) - throw new ArgumentNullException (nameof (content)); - - if (signatureData == null) - throw new ArgumentNullException (nameof (signatureData)); - - using (var armored = new ArmoredInputStream (signatureData)) { - var factory = new PgpObjectFactory (armored); - var data = factory.NextPgpObject (); - PgpSignatureList signatureList; - - var compressed = data as PgpCompressedData; - if (compressed != null) { - factory = new PgpObjectFactory (compressed.GetDataStream ()); - data = factory.NextPgpObject (); - } - - if (data == null) - throw new FormatException ("Invalid PGP format."); - - signatureList = (PgpSignatureList) data; - - return GetDigitalSignaturesAsync (signatureList, content, doAsync, cancellationToken); - } - } - - /// - /// Verify the specified content using the detached signatureData. - /// - /// - /// Verifies the specified content using the detached signatureData. - /// If any of the signatures were made with an unrecognized key and is enabled, - /// an attempt will be made to retrieve said key(s). The can be used to cancel - /// key retrieval. - /// - /// A list of digital signatures. - /// The content. - /// The signature data. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain valid PGP signature data. - /// - public override DigitalSignatureCollection Verify (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (content, signatureData, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously verify the specified content using the detached signatureData. - /// - /// - /// Verifies the specified content using the detached signatureData. - /// If any of the signatures were made with an unrecognized key and is enabled, - /// an attempt will be made to retrieve said key(s). The can be used to cancel - /// key retrieval. - /// - /// A list of digital signatures. - /// The content. - /// The signature data. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain valid PGP signature data. - /// - public override Task VerifyAsync (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (content, signatureData, true, cancellationToken); - } - - static Stream Compress (Stream content, byte[] buf) - { - var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); - var memory = new MemoryBlockStream (); - - using (var compressed = compresser.Open (memory)) { - var literalGenerator = new PgpLiteralDataGenerator (); - - using (var literal = literalGenerator.Open (compressed, 't', "mime.txt", content.Length, DateTime.Now)) { - int nread; - - while ((nread = content.Read (buf, 0, buf.Length)) > 0) - literal.Write (buf, 0, nread); - - literal.Flush (); - } - - compressed.Flush (); - } - - memory.Position = 0; - - return memory; - } - - static Stream Encrypt (PgpEncryptedDataGenerator encrypter, Stream content) - { - var memory = new MemoryBlockStream (); - - using (var armored = new ArmoredOutputStream (memory)) { - var buf = new byte[4096]; - - armored.SetHeader ("Version", null); - - using (var compressed = Compress (content, buf)) { - using (var encrypted = encrypter.Open (armored, compressed.Length)) { - int nread; - - while ((nread = compressed.Read (buf, 0, buf.Length)) > 0) - encrypted.Write (buf, 0, nread); - - encrypted.Flush (); - } - } - - armored.Flush (); - } - - memory.Position = 0; - - return memory; - } - - internal static SymmetricKeyAlgorithmTag GetSymmetricKeyAlgorithm (EncryptionAlgorithm algorithm) - { - switch (algorithm) { - case EncryptionAlgorithm.Aes128: return SymmetricKeyAlgorithmTag.Aes128; - case EncryptionAlgorithm.Aes192: return SymmetricKeyAlgorithmTag.Aes192; - case EncryptionAlgorithm.Aes256: return SymmetricKeyAlgorithmTag.Aes256; - case EncryptionAlgorithm.Camellia128: return SymmetricKeyAlgorithmTag.Camellia128; - case EncryptionAlgorithm.Camellia192: return SymmetricKeyAlgorithmTag.Camellia192; - case EncryptionAlgorithm.Camellia256: return SymmetricKeyAlgorithmTag.Camellia256; - case EncryptionAlgorithm.Cast5: return SymmetricKeyAlgorithmTag.Cast5; - case EncryptionAlgorithm.Des: return SymmetricKeyAlgorithmTag.Des; - case EncryptionAlgorithm.TripleDes: return SymmetricKeyAlgorithmTag.TripleDes; - case EncryptionAlgorithm.Idea: return SymmetricKeyAlgorithmTag.Idea; - case EncryptionAlgorithm.Blowfish: return SymmetricKeyAlgorithmTag.Blowfish; - case EncryptionAlgorithm.Twofish: return SymmetricKeyAlgorithmTag.Twofish; - default: throw new NotSupportedException (string.Format ("{0} is not supported.", algorithm)); - } - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// A public key could not be found for one or more of the . - /// - public override MimePart Encrypt (IEnumerable recipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - // TODO: document the exceptions that can be thrown by BouncyCastle - return Encrypt (GetPublicKeys (recipients), content); - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The encryption algorithm. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// A public key could not be found for one or more of the . - /// - /// - /// The specified encryption algorithm is not supported. - /// - public MimePart Encrypt (EncryptionAlgorithm algorithm, IEnumerable recipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - // TODO: document the exceptions that can be thrown by BouncyCastle - return Encrypt (algorithm, GetPublicKeys (recipients), content); - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The encryption algorithm. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The specified encryption algorithm is not supported. - /// - public MimePart Encrypt (EncryptionAlgorithm algorithm, IEnumerable recipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var encrypter = new PgpEncryptedDataGenerator (GetSymmetricKeyAlgorithm (algorithm), true); - var unique = new HashSet (); - int count = 0; - - foreach (var recipient in recipients) { - if (!recipient.IsEncryptionKey) - throw new ArgumentException ("One or more of the recipient keys cannot be used for encrypting.", nameof (recipients)); - - if (unique.Add (recipient.KeyId)) { - encrypter.AddMethod (recipient); - count++; - } - } - - if (count == 0) - throw new ArgumentException ("No recipients specified.", nameof (recipients)); - - var encrypted = Encrypt (encrypter, content); - - return new MimePart ("application", "octet-stream") { - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment), - Content = new MimeContent (encrypted), - }; - } - - /// - /// Encrypt the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - public MimePart Encrypt (IEnumerable recipients, Stream content) - { - return Encrypt (defaultAlgorithm, recipients, content); - } - - /// - /// Cryptographically sign and encrypt the specified content for the specified recipients. - /// - /// - /// Cryptographically signs and encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The signer. - /// The digest algorithm to use for signing. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The specified is not supported by this context. - /// - /// - /// The private key could not be found for . - /// - /// - /// A public key could not be found for one or more of the . - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public MimePart SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, IEnumerable recipients, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var key = GetSigningKey (signer); - - return SignAndEncrypt (key, digestAlgo, GetPublicKeys (recipients), content); - } - - /// - /// Cryptographically sign and encrypt the specified content for the specified recipients. - /// - /// - /// Cryptographically signs and encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The signer. - /// The digest algorithm to use for signing. - /// The encryption algorithm. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The specified encryption algorithm is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public MimePart SignAndEncrypt (MailboxAddress signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerable recipients, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var key = GetSigningKey (signer); - - return SignAndEncrypt (key, digestAlgo, cipherAlgo, GetPublicKeys (recipients), content); - } - - /// - /// Cryptographically sign and encrypt the specified content for the specified recipients. - /// - /// - /// Cryptographically signs and encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The signer. - /// The digest algorithm to use for signing. - /// The encryption algorithm. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The specified encryption algorithm is not supported. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public MimePart SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, EncryptionAlgorithm cipherAlgo, IEnumerable recipients, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (!signer.IsSigningKey) - throw new ArgumentException ("The specified secret key cannot be used for signing.", nameof (signer)); - - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var encrypter = new PgpEncryptedDataGenerator (GetSymmetricKeyAlgorithm (cipherAlgo), true); - var hashAlgorithm = GetHashAlgorithm (digestAlgo); - var unique = new HashSet (); - var buf = new byte[4096]; - int nread, count = 0; - - foreach (var recipient in recipients) { - if (!recipient.IsEncryptionKey) - throw new ArgumentException ("One or more of the recipient keys cannot be used for encrypting.", nameof (recipients)); - - if (unique.Add (recipient.KeyId)) { - encrypter.AddMethod (recipient); - count++; - } - } - - if (count == 0) - throw new ArgumentException ("No recipients specified.", nameof (recipients)); - - var compresser = new PgpCompressedDataGenerator (CompressionAlgorithmTag.ZLib); - - using (var compressed = new MemoryBlockStream ()) { - using (var signed = compresser.Open (compressed)) { - var signatureGenerator = new PgpSignatureGenerator (signer.PublicKey.Algorithm, hashAlgorithm); - signatureGenerator.InitSign (PgpSignature.CanonicalTextDocument, GetPrivateKey (signer)); - var subpacket = new PgpSignatureSubpacketGenerator (); - - foreach (string userId in signer.PublicKey.GetUserIds ()) { - subpacket.SetSignerUserId (false, userId); - break; - } - - signatureGenerator.SetHashedSubpackets (subpacket.Generate ()); - - var onepass = signatureGenerator.GenerateOnePassVersion (false); - onepass.Encode (signed); - - var literalGenerator = new PgpLiteralDataGenerator (); - using (var literal = literalGenerator.Open (signed, 't', "mime.txt", content.Length, DateTime.Now)) { - while ((nread = content.Read (buf, 0, buf.Length)) > 0) { - signatureGenerator.Update (buf, 0, nread); - literal.Write (buf, 0, nread); - } - - literal.Flush (); - } - - var signature = signatureGenerator.Generate (); - signature.Encode (signed); - - signed.Flush (); - } - - compressed.Position = 0; - - var memory = new MemoryBlockStream (); - - using (var armored = new ArmoredOutputStream (memory)) { - armored.SetHeader ("Version", null); - - using (var encrypted = encrypter.Open (armored, compressed.Length)) { - while ((nread = compressed.Read (buf, 0, buf.Length)) > 0) - encrypted.Write (buf, 0, nread); - - encrypted.Flush (); - } - - armored.Flush (); - } - - memory.Position = 0; - - return new MimePart ("application", "octet-stream") { - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment), - Content = new MimeContent (memory) - }; - } - } - - /// - /// Cryptographically sign and encrypt the specified content for the specified recipients. - /// - /// - /// Cryptographically signs and encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The signer. - /// The digest algorithm to use for signing. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be used for signing. - /// -or- - /// One or more of the recipient keys cannot be used for encrypting. - /// -or- - /// No recipients were specified. - /// - /// - /// The user chose to cancel the password prompt. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - public MimePart SignAndEncrypt (PgpSecretKey signer, DigestAlgorithm digestAlgo, IEnumerable recipients, Stream content) - { - return SignAndEncrypt (signer, digestAlgo, defaultAlgorithm, recipients, content); - } - - async Task DecryptToAsync (Stream encryptedData, Stream decryptedData, bool doAsync, CancellationToken cancellationToken) - { - if (encryptedData == null) - throw new ArgumentNullException (nameof (encryptedData)); - - if (decryptedData == null) - throw new ArgumentNullException (nameof (decryptedData)); - - using (var armored = new ArmoredInputStream (encryptedData)) { - var factory = new PgpObjectFactory (armored); - var obj = factory.NextPgpObject (); - var list = obj as PgpEncryptedDataList; - - if (list == null) { - // probably a PgpMarker... - obj = factory.NextPgpObject (); - - list = obj as PgpEncryptedDataList; - - if (list == null) - throw new PgpException ("Unexpected OpenPGP packet."); - } - - PgpPublicKeyEncryptedData encrypted = null; - PrivateKeyNotFoundException pkex = null; - bool hasEncryptedPackets = false; - PgpSecretKey secret = null; - - foreach (PgpEncryptedData data in list.GetEncryptedDataObjects ()) { - if ((encrypted = data as PgpPublicKeyEncryptedData) == null) - continue; - - hasEncryptedPackets = true; - - try { - secret = GetSecretKey (encrypted.KeyId); - break; - } catch (PrivateKeyNotFoundException ex) { - pkex = ex; - } - } - - if (!hasEncryptedPackets) - throw new PgpException ("No encrypted packets found."); - - if (secret == null) - throw pkex; - - factory = new PgpObjectFactory (encrypted.GetDataStream (GetPrivateKey (secret))); - List onepassList = null; - DigitalSignatureCollection signatures; - PgpSignatureList signatureList = null; - PgpCompressedData compressed = null; - var position = decryptedData.Position; - long nwritten = 0; - - obj = factory.NextPgpObject (); - while (obj != null) { - if (obj is PgpCompressedData) { - if (compressed != null) - throw new PgpException ("Recursive compression packets are not supported."); - - compressed = (PgpCompressedData) obj; - factory = new PgpObjectFactory (compressed.GetDataStream ()); - } else if (obj is PgpOnePassSignatureList) { - if (nwritten == 0) { - var onepasses = (PgpOnePassSignatureList) obj; - - onepassList = new List (); - - for (int i = 0; i < onepasses.Count; i++) { - var onepass = onepasses[i]; - PgpPublicKeyRing keyring; - - if (doAsync) - keyring = await GetPublicKeyRingAsync (onepass.KeyId, cancellationToken).ConfigureAwait (false); - else - keyring = GetPublicKeyRing (onepass.KeyId, cancellationToken); - - if (!TryGetPublicKey (keyring, onepass.KeyId, out var key)) { - // too messy, pretend we never found a one-pass signature list - onepassList = null; - break; - } - - onepass.InitVerify (key); - - var signature = new OpenPgpDigitalSignature (keyring, key, onepass) { - PublicKeyAlgorithm = GetPublicKeyAlgorithm (onepass.KeyAlgorithm), - DigestAlgorithm = GetDigestAlgorithm (onepass.HashAlgorithm), - }; - - onepassList.Add (signature); - } - } - } else if (obj is PgpSignatureList) { - signatureList = (PgpSignatureList) obj; - } else if (obj is PgpLiteralData) { - var literal = (PgpLiteralData) obj; - - using (var stream = literal.GetDataStream ()) { - var buffer = new byte[4096]; - int nread; - - while ((nread = stream.Read (buffer, 0, buffer.Length)) > 0) { - if (onepassList != null) { - // update our one-pass signatures... - for (int index = 0; index < nread; index++) { - byte c = buffer[index]; - - for (int i = 0; i < onepassList.Count; i++) { - var pgp = (OpenPgpDigitalSignature) onepassList[i]; - pgp.OnePassSignature.Update (c); - } - } - } - - if (doAsync) - await decryptedData.WriteAsync (buffer, 0, nread, cancellationToken).ConfigureAwait (false); - else - decryptedData.Write (buffer, 0, nread); - - nwritten += nread; - } - } - } - - obj = factory.NextPgpObject (); - } - - if (signatureList != null) { - if (onepassList != null && signatureList.Count == onepassList.Count) { - for (int i = 0; i < onepassList.Count; i++) { - var pgp = (OpenPgpDigitalSignature) onepassList[i]; - pgp.CreationDate = signatureList[i].CreationTime; - pgp.Signature = signatureList[i]; - } - - signatures = new DigitalSignatureCollection (onepassList); - } else { - decryptedData.Position = position; - signatures = await GetDigitalSignaturesAsync (signatureList, decryptedData, doAsync, cancellationToken).ConfigureAwait (false); - decryptedData.Position = decryptedData.Length; - } - } else { - signatures = null; - } - - return signatures; - } - } - - /// - /// Decrypt an encrypted stream and extract the digital signers if the content was also signed. - /// - /// - /// Decrypts an encrypted stream and extracts the digital signers if the content was also signed. - /// If any of the signatures were made with an unrecognized key and is enabled, - /// an attempt will be made to retrieve said key(s). The can be used to cancel - /// key retrieval. - /// - /// The list of digital signatures if the data was both signed and encrypted; otherwise, null. - /// The encrypted data. - /// The stream to write the decrypted data to. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The private key could not be found to decrypt the stream. - /// - /// - /// The user chose to cancel the password prompt. - /// -or- - /// The operation was cancelled via the cancellation token. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - /// - /// An OpenPGP error occurred. - /// - public DigitalSignatureCollection DecryptTo (Stream encryptedData, Stream decryptedData, CancellationToken cancellationToken = default (CancellationToken)) - { - return DecryptToAsync (encryptedData, decryptedData, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously decrypt an encrypted stream and extract the digital signers if the content was also signed. - /// - /// - /// Decrypts an encrypted stream and extracts the digital signers if the content was also signed. - /// If any of the signatures were made with an unrecognized key and is enabled, - /// an attempt will be made to retrieve said key(s). The can be used to cancel - /// key retrieval. - /// - /// The list of digital signatures if the data was both signed and encrypted; otherwise, null. - /// The encrypted data. - /// The stream to write the decrypted data to. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The private key could not be found to decrypt the stream. - /// - /// - /// The user chose to cancel the password prompt. - /// -or- - /// The operation was cancelled via the cancellation token. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - /// - /// An OpenPGP error occurred. - /// - public Task DecryptToAsync (Stream encryptedData, Stream decryptedData, CancellationToken cancellationToken = default (CancellationToken)) - { - return DecryptToAsync (encryptedData, decryptedData, true, cancellationToken); - } - - /// - /// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed. - /// - /// - /// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed. - /// - /// The decrypted . - /// The encrypted data. - /// A list of digital signatures if the data was both signed and encrypted. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The private key could not be found to decrypt the stream. - /// - /// - /// The user chose to cancel the password prompt. - /// -or- - /// The operation was cancelled via the cancellation token. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - /// - /// An OpenPGP error occurred. - /// - public MimeEntity Decrypt (Stream encryptedData, out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) - { - using (var decryptedData = new MemoryBlockStream ()) { - signatures = DecryptTo (encryptedData, decryptedData, cancellationToken); - decryptedData.Position = 0; - - return MimeEntity.Load (decryptedData, cancellationToken); - } - } - - /// - /// Decrypts the specified encryptedData. - /// - /// - /// Decrypts the specified encryptedData. - /// - /// The decrypted . - /// The encrypted data. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The private key could not be found to decrypt the stream. - /// - /// - /// The user chose to cancel the password prompt. - /// -or- - /// The operation was cancelled via the cancellation token. - /// - /// - /// 3 bad attempts were made to unlock the secret key. - /// - /// - /// An OpenPGP error occurred. - /// - public override MimeEntity Decrypt (Stream encryptedData, CancellationToken cancellationToken = default (CancellationToken)) - { - using (var decryptedData = new MemoryBlockStream ()) { - DecryptTo (encryptedData, decryptedData, cancellationToken); - decryptedData.Position = 0; - - return MimeEntity.Load (decryptedData, cancellationToken); - } - } - - /// - /// Import the specified public keyring bundle. - /// - /// - /// Imports the specified public keyring bundle. - /// - /// THe bundle of public keyrings to import. - public abstract void Import (PgpPublicKeyRingBundle bundle); - - /// - /// Releases all resources used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. - protected override void Dispose (bool disposing) - { - if (disposing && client != null) { - client.Dispose (); - client = null; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MimeKit/Cryptography/OpenPgpDataType.cs b/src/MimeKit/Cryptography/OpenPgpDataType.cs deleted file mode 100644 index 55cdc30..0000000 --- a/src/MimeKit/Cryptography/OpenPgpDataType.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// OpenPgpDataType.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography -{ - /// - /// An enum expressing a type of OpenPGP data. - /// - /// - /// An enum expressing a type of OpenPGP data. - /// - public enum OpenPgpDataType - { - /// - /// No OpenPGP data detected. - /// - None, - - /// - /// The OpenPGP data is a signed message. - /// - SignedMessage, - - /// - /// The OpenPGP data is an encrypted message. - /// - EncryptedMessage, - - /// - /// The OpenPGP data is a public key. - /// - PublicKey, - - /// - /// The OpenPGP data is a private key. - /// - PrivateKey - } -} diff --git a/src/MimeKit/Cryptography/OpenPgpDetectionFilter.cs b/src/MimeKit/Cryptography/OpenPgpDetectionFilter.cs deleted file mode 100644 index ea9c757..0000000 --- a/src/MimeKit/Cryptography/OpenPgpDetectionFilter.cs +++ /dev/null @@ -1,344 +0,0 @@ -// -// OpenPgpDetectionFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MimeKit.Utils; -using MimeKit.IO.Filters; - -namespace MimeKit.Cryptography -{ - /// - /// A filter meant to aid in the detection of OpenPGP blocks. - /// - /// - /// Detects OpenPGP block markers and their byte offsets. - /// - public class OpenPgpDetectionFilter : MimeFilterBase - { - enum OpenPgpState { - None = 0, - BeginPgpMessage = (1 << 0), - EndPgpMessage = (1 << 1) | (1 << 0), - BeginPgpSignedMessage = (1 << 2), - BeginPgpSignature = (1 << 3) | (1 << 2), - EndPgpSignature = (1 << 4) | (1 << 3) | (1 << 2), - BeginPgpPublicKeyBlock = (1 << 5), - EndPgpPublicKeyBlock = (1 << 6) | (1 << 5), - BeginPgpPrivateKeyBlock = (1 << 7), - EndPgpPrivateKeyBlock = (1 << 8) | (1 << 7) - } - - struct OpenPgpMarker - { - public byte[] Marker; - public OpenPgpState InitialState; - public OpenPgpState DetectedState; - public bool IsEnd; - - public OpenPgpMarker (string marker, OpenPgpState initial, OpenPgpState detected, bool isEnd) - { - Marker = CharsetUtils.UTF8.GetBytes (marker); - InitialState = initial; - DetectedState = detected; - IsEnd = isEnd; - } - } - - static readonly OpenPgpMarker[] OpenPgpMarkers = { - new OpenPgpMarker ("-----BEGIN PGP MESSAGE-----", OpenPgpState.None, OpenPgpState.BeginPgpMessage, false), - new OpenPgpMarker ("-----END PGP MESSAGE-----", OpenPgpState.BeginPgpMessage, OpenPgpState.EndPgpMessage, true), - new OpenPgpMarker ("-----BEGIN PGP SIGNED MESSAGE-----", OpenPgpState.None, OpenPgpState.BeginPgpSignedMessage, false), - new OpenPgpMarker ("-----BEGIN PGP SIGNATURE-----", OpenPgpState.BeginPgpSignedMessage, OpenPgpState.BeginPgpSignature, false), - new OpenPgpMarker ("-----END PGP SIGNATURE-----", OpenPgpState.BeginPgpSignature, OpenPgpState.EndPgpSignature, true), - new OpenPgpMarker ("-----BEGIN PGP PUBLIC KEY BLOCK-----", OpenPgpState.None, OpenPgpState.BeginPgpPublicKeyBlock, false), - new OpenPgpMarker ("-----END PGP PUBLIC KEY BLOCK-----", OpenPgpState.BeginPgpPublicKeyBlock, OpenPgpState.EndPgpPublicKeyBlock, true), - new OpenPgpMarker ("-----BEGIN PGP PRIVATE KEY BLOCK-----", OpenPgpState.None, OpenPgpState.BeginPgpPrivateKeyBlock, false), - new OpenPgpMarker ("-----END PGP PRIVATE KEY BLOCK-----", OpenPgpState.BeginPgpPrivateKeyBlock, OpenPgpState.EndPgpPrivateKeyBlock, false) - }; - - OpenPgpState state; - int position, next; - bool seenEndMarker; - bool midline; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public OpenPgpDetectionFilter () - { - } - - /// - /// Get the byte offset of the BEGIN marker, if available. - /// - /// - /// Gets the byte offset of the BEGIN marker if available. - /// - /// The byte offset. - public int? BeginOffset { - get; private set; - } - - /// - /// Get the byte offset of the END marker, if available. - /// - /// - /// Gets the byte offset of the END marker if available. - /// - /// The byte offset. - public int? EndOffset { - get; private set; - } - - /// - /// Get the type of OpenPGP data detected. - /// - /// - /// Gets the type of OpenPGP data detected. - /// - /// The type of OpenPGP data detected. - public OpenPgpDataType DataType { - get { - switch (state) { - case OpenPgpState.EndPgpPrivateKeyBlock: return OpenPgpDataType.PrivateKey; - case OpenPgpState.EndPgpPublicKeyBlock: return OpenPgpDataType.PublicKey; - case OpenPgpState.EndPgpSignature: return OpenPgpDataType.SignedMessage; - case OpenPgpState.EndPgpMessage: return OpenPgpDataType.EncryptedMessage; - default: return OpenPgpDataType.None; - } - } - } - - static bool IsMarker (byte[] input, int startIndex, int endIndex, byte[] marker, out bool cr) - { - int i = startIndex; - int j = 0; - - cr = false; - - while (j < marker.Length && i < endIndex) { - if (input[i++] != marker[j++]) - return false; - } - - if (j < marker.Length) - return false; - - if (i < endIndex && input[i] == (byte) '\r') { - cr = true; - i++; - } - - return i < endIndex && input[i] == (byte) '\n'; - } - - static bool IsPartialMatch (byte[] input, int startIndex, int endIndex, byte[] marker) - { - int i = startIndex; - int j = 0; - - while (j < marker.Length && i < endIndex) { - if (input[i++] != marker[j++]) - return false; - } - - if (i < endIndex && input[i] == (byte) '\r') - i++; - - return i == endIndex; - } - - void SetPosition (int offset, int marker, bool cr) - { - int length = OpenPgpMarkers[marker].Marker.Length + (cr ? 2 : 1); - - switch (state) { - case OpenPgpState.BeginPgpPrivateKeyBlock: BeginOffset = position + offset; break; - case OpenPgpState.EndPgpPrivateKeyBlock: EndOffset = position + offset + length; break; - case OpenPgpState.BeginPgpPublicKeyBlock: BeginOffset = position + offset; break; - case OpenPgpState.EndPgpPublicKeyBlock: EndOffset = position + offset + length; break; - case OpenPgpState.BeginPgpSignedMessage: BeginOffset = position + offset; break; - case OpenPgpState.EndPgpSignature: EndOffset = position + offset + length; break; - case OpenPgpState.BeginPgpMessage: BeginOffset = position + offset; break; - case OpenPgpState.EndPgpMessage: EndOffset = position + offset + length; break; - } - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - int endIndex = startIndex + length; - int index = startIndex; - bool cr; - - outputIndex = startIndex; - outputLength = 0; - - if (seenEndMarker || length == 0) - return input; - - if (midline) { - while (index < endIndex && input[index] != (byte) '\n') - index++; - - if (index == endIndex) { - if (state != OpenPgpState.None) - outputLength = index - startIndex; - - position += index - startIndex; - - return input; - } - - midline = false; - } - - if (state == OpenPgpState.None) { - do { - int lineIndex = index; - - while (index < endIndex && input[index] != (byte) '\n') - index++; - - if (index == endIndex) { - bool isPartialMatch = false; - - for (int i = 0; i < OpenPgpMarkers.Length; i++) { - if (OpenPgpMarkers[i].InitialState == state && IsPartialMatch (input, lineIndex, index, OpenPgpMarkers[i].Marker)) { - isPartialMatch = true; - break; - } - } - - if (isPartialMatch) { - SaveRemainingInput (input, lineIndex, index - lineIndex); - position += lineIndex - startIndex; - } else { - position += index - lineIndex; - midline = true; - } - - return input; - } - - index++; - - for (int i = 0; i < OpenPgpMarkers.Length; i++) { - if (OpenPgpMarkers[i].InitialState == state && IsMarker (input, lineIndex, endIndex, OpenPgpMarkers[i].Marker, out cr)) { - state = OpenPgpMarkers[i].DetectedState; - SetPosition (lineIndex - startIndex, i, cr); - outputLength = index - lineIndex; - outputIndex = lineIndex; - next = i + 1; - break; - } - } - } while (index < endIndex && state == OpenPgpState.None); - - if (index == endIndex) { - position += index - startIndex; - return input; - } - } - - do { - int lineIndex = index; - - while (index < endIndex && input[index] != (byte) '\n') - index++; - - if (index == endIndex) { - if (!flush) { - if (IsPartialMatch (input, lineIndex, index, OpenPgpMarkers[next].Marker)) { - SaveRemainingInput (input, lineIndex, index - lineIndex); - outputLength = lineIndex - outputIndex; - position += lineIndex - startIndex; - } else { - outputLength = index - outputIndex; - position += index - startIndex; - midline = true; - } - - return input; - } - - break; - } - - index++; - - if (IsMarker (input, lineIndex, endIndex, OpenPgpMarkers[next].Marker, out cr)) { - seenEndMarker = OpenPgpMarkers[next].IsEnd; - state = OpenPgpMarkers[next].DetectedState; - SetPosition (lineIndex - startIndex, next, cr); - next++; - - if (seenEndMarker) - break; - } - } while (index < endIndex); - - outputLength = index - outputIndex; - position += index - startIndex; - - return input; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - state = OpenPgpState.None; - seenEndMarker = false; - BeginOffset = null; - EndOffset = null; - midline = false; - position = 0; - next = 0; - - base.Reset (); - } - } -} diff --git a/src/MimeKit/Cryptography/OpenPgpDigitalCertificate.cs b/src/MimeKit/Cryptography/OpenPgpDigitalCertificate.cs deleted file mode 100644 index bba0c86..0000000 --- a/src/MimeKit/Cryptography/OpenPgpDigitalCertificate.cs +++ /dev/null @@ -1,184 +0,0 @@ -// -// OpenPgpDigitalCertificate.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -using Org.BouncyCastle.Bcpg.OpenPgp; - -namespace MimeKit.Cryptography { - /// - /// An OpenPGP digital certificate. - /// - /// - /// An OpenPGP digital certificate. - /// - public class OpenPgpDigitalCertificate : IDigitalCertificate - { - internal OpenPgpDigitalCertificate (PgpPublicKeyRing keyring, PgpPublicKey pubkey) - { - var bytes = pubkey.GetFingerprint (); - var builder = new StringBuilder (); - - for (int i = 0; i < bytes.Length; i++) - builder.Append (bytes[i].ToString ("X2")); - -// var trust = pubkey.GetTrustData (); -// if (trust != null) { -// TrustLevel = (TrustLevel) (trust[0] & 15); -// } else { -// TrustLevel = TrustLevel.None; -// } - - Fingerprint = builder.ToString (); - PublicKey = pubkey; - KeyRing = keyring; - - if (!UpdateUserId (pubkey) && !pubkey.IsMasterKey) { - foreach (PgpPublicKey key in keyring.GetPublicKeys ()) { - if (key.IsMasterKey) { - UpdateUserId (key); - break; - } - } - } - } - - bool UpdateUserId (PgpPublicKey pubkey) - { - foreach (string userId in pubkey.GetUserIds ()) { - var bytes = Encoding.UTF8.GetBytes (userId); - MailboxAddress mailbox; - int index = 0; - - if (!MailboxAddress.TryParse (ParserOptions.Default, bytes, ref index, bytes.Length, false, out mailbox)) - continue; - - Email = mailbox.Address; - Name = mailbox.Name; - return true; - } - - return false; - } - - /// - /// Gets the public key ring. - /// - /// - /// Get the public key ring that is associated with. - /// - /// The key ring. - public PgpPublicKeyRing KeyRing { - get; private set; - } - - /// - /// Gets the public key. - /// - /// - /// Get the public key. - /// - /// The public key. - public PgpPublicKey PublicKey { - get; private set; - } - - #region IDigitalCertificate implementation - - /// - /// Gets the public key algorithm supported by the certificate. - /// - /// - /// Gets the public key algorithm supported by the certificate. - /// - /// The public key algorithm. - public PublicKeyAlgorithm PublicKeyAlgorithm { - get { return OpenPgpContext.GetPublicKeyAlgorithm (PublicKey.Algorithm); } - } - - /// - /// Gets the date that the certificate was created. - /// - /// - /// Gets the date that the certificate was created. - /// - /// The creation date. - public DateTime CreationDate { - get { return PublicKey.CreationTime; } - } - - /// - /// Gets the expiration date of the certificate. - /// - /// - /// Gets the expiration date of the certificate. - /// - /// The expiration date. - public DateTime ExpirationDate { - get { - long seconds = PublicKey.GetValidSeconds (); - - return seconds > 0 ? CreationDate.AddSeconds ((double) seconds) : DateTime.MaxValue; - } - } - - /// - /// Gets the fingerprint of the certificate. - /// - /// - /// Gets the fingerprint of the certificate. - /// - /// The fingerprint. - public string Fingerprint { - get; private set; - } - - /// - /// Gets the email address of the owner of the certificate. - /// - /// - /// Gets the email address of the owner of the certificate. - /// - /// The email address. - public string Email { - get; private set; - } - - /// - /// Gets the name of the owner of the certificate. - /// - /// - /// Gets the name of the owner of the certificate. - /// - /// The name of the owner. - public string Name { - get; private set; - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/OpenPgpDigitalSignature.cs b/src/MimeKit/Cryptography/OpenPgpDigitalSignature.cs deleted file mode 100644 index 63073ff..0000000 --- a/src/MimeKit/Cryptography/OpenPgpDigitalSignature.cs +++ /dev/null @@ -1,164 +0,0 @@ -// -// OpenPgpDigitalSignature.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using Org.BouncyCastle.Bcpg.OpenPgp; - -namespace MimeKit.Cryptography { - /// - /// An OpenPGP digital signature. - /// - /// - /// An OpenPGP digital signature. - /// - public class OpenPgpDigitalSignature : IDigitalSignature - { - DigitalSignatureVerifyException vex; - bool? valid; - - internal OpenPgpDigitalSignature (PgpPublicKeyRing keyring, PgpPublicKey pubkey, PgpOnePassSignature signature) - { - SignerCertificate = pubkey != null ? new OpenPgpDigitalCertificate (keyring, pubkey) : null; - OnePassSignature = signature; - } - - internal OpenPgpDigitalSignature (PgpPublicKeyRing keyring, PgpPublicKey pubkey, PgpSignature signature) - { - SignerCertificate = pubkey != null ? new OpenPgpDigitalCertificate (keyring, pubkey) : null; - Signature = signature; - } - - internal PgpOnePassSignature OnePassSignature { - get; private set; - } - - internal PgpSignature Signature { - get; set; - } - - #region IDigitalSignature implementation - - /// - /// Gets certificate used by the signer. - /// - /// - /// Gets certificate used by the signer. - /// - /// The signer's certificate. - public IDigitalCertificate SignerCertificate { - get; private set; - } - - /// - /// Gets the public key algorithm used for the signature. - /// - /// - /// Gets the public key algorithm used for the signature. - /// - /// The public key algorithm. - public PublicKeyAlgorithm PublicKeyAlgorithm { - get; internal set; - } - - /// - /// Gets the digest algorithm used for the signature. - /// - /// - /// Gets the digest algorithm used for the signature. - /// - /// The digest algorithm. - public DigestAlgorithm DigestAlgorithm { - get; internal set; - } - - /// - /// Gets the creation date of the digital signature. - /// - /// - /// Gets the creation date of the digital signature. - /// - /// The creation date. - public DateTime CreationDate { - get; internal set; - } - - /// - /// Verifies the digital signature. - /// - /// - /// Verifies the digital signature. - /// - /// true if the signature is valid; otherwise, false. - /// - /// An error verifying the signature has occurred. - /// - public bool Verify () - { - if (valid.HasValue) - return valid.Value; - - if (vex != null) - throw vex; - - if (SignerCertificate == null) { - var message = string.Format ("Failed to verify digital signature: no public key found for {0:X8}", (int) Signature.KeyId); - vex = new DigitalSignatureVerifyException (Signature.KeyId, message); - throw vex; - } - - try { - if (OnePassSignature != null) - valid = OnePassSignature.Verify (Signature); - else - valid = Signature.Verify (); - return valid.Value; - } catch (Exception ex) { - var message = string.Format ("Failed to verify digital signature: {0}", ex.Message); - vex = new DigitalSignatureVerifyException (Signature.KeyId, message, ex); - throw vex; - } - } - - /// - /// Verifies the digital signature. - /// - /// - /// Verifies the digital signature. - /// - /// This option is ignored for OpenPGP digital signatures. - /// true if the signature is valid; otherwise, false. - /// - /// An error verifying the signature has occurred. - /// - public bool Verify (bool verifySignatureOnly) - { - return Verify (); - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/OpenPgpKeyCertification.cs b/src/MimeKit/Cryptography/OpenPgpKeyCertification.cs deleted file mode 100644 index 656315b..0000000 --- a/src/MimeKit/Cryptography/OpenPgpKeyCertification.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -// OpenPgpKeyCertification.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography -{ - /// - /// An OpenPGP key certification. - /// - /// - /// An OpenPGP key certification. - /// - public enum OpenPgpKeyCertification { - /// - /// Generic certification of a User ID and Public-Key packet. - /// The issuer of this certification does not make any particular - /// assertion as to how well the certifier has checked that the owner - /// of the key is in fact the person described by the User ID. - /// - GenericCertification = 0x10, - - /// - /// Persona certification of a User ID and Public-Key packet. - /// The issuer of this certification has not done any verification of - /// the claim that the owner of this key is the User ID specified. - /// - PersonaCertification = 0x11, - - /// - /// Casual certification of a User ID and Public-Key packet. - /// The issuer of this certification has done some casual - /// verification of the claim of identity. - /// - CasualCertification = 0x12, - - /// - /// Positive certification of a User ID and Public-Key packet. - /// The issuer of this certification has done substantial - /// verification of the claim of identity. - /// - PositiveCertification = 0x13 - } -} diff --git a/src/MimeKit/Cryptography/PrivateKeyNotFoundException.cs b/src/MimeKit/Cryptography/PrivateKeyNotFoundException.cs deleted file mode 100644 index b4bf7bb..0000000 --- a/src/MimeKit/Cryptography/PrivateKeyNotFoundException.cs +++ /dev/null @@ -1,152 +0,0 @@ -// -// PrivateKeyNotFoundException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MimeKit.Cryptography { - /// - /// An exception that is thrown when a private key could not be found for a specified mailbox or key id. - /// - /// - /// An exception that is thrown when a private key could not be found for a specified mailbox or key id. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class PrivateKeyNotFoundException : Exception - { -#if SERIALIZABLE - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The serialization info. - /// The stream context. - /// - /// is null. - /// - protected PrivateKeyNotFoundException (SerializationInfo info, StreamingContext context) : base (info, context) - { - KeyId = info.GetString ("KeyId"); - } -#endif - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The mailbox that could not be resolved to a valid private key. - /// A message explaining the error. - /// - /// is null. - /// - public PrivateKeyNotFoundException (MailboxAddress mailbox, string message) : base (message) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - KeyId = mailbox.Address; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The key id that could not be resolved to a valid certificate. - /// A message explaining the error. - /// - /// is null. - /// - public PrivateKeyNotFoundException (string keyid, string message) : base (message) - { - if (keyid == null) - throw new ArgumentNullException (nameof (keyid)); - - KeyId = keyid; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The key id that could not be resolved to a valid certificate. - /// A message explaining the error. - /// - /// is null. - /// - public PrivateKeyNotFoundException (long keyid, string message) : base (message) - { - KeyId = keyid.ToString ("X"); - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Sets the - /// with information about the exception. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("KeyId", KeyId); - } -#endif - - /// - /// Gets the key id that could not be found. - /// - /// - /// Gets the key id that could not be found. - /// - /// The key id. - public string KeyId { - get; private set; - } - } -} diff --git a/src/MimeKit/Cryptography/PublicKeyAlgorithm.cs b/src/MimeKit/Cryptography/PublicKeyAlgorithm.cs deleted file mode 100644 index 8ece4b6..0000000 --- a/src/MimeKit/Cryptography/PublicKeyAlgorithm.cs +++ /dev/null @@ -1,90 +0,0 @@ -// -// PublicKeyAlgorithm.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// An enumeration of public key algorithms. - /// - /// - /// An enumeration of public key algorithms. - /// - public enum PublicKeyAlgorithm { - /// - /// No public key algorithm specified. - /// - None = 0, - - /// - /// The RSA algorithm. - /// - RsaGeneral = 1, - - /// - /// The RSA encryption-only algorithm. - /// - RsaEncrypt = 2, - - /// - /// The RSA sign-only algorithm. - /// - RsaSign = 3, - - /// - /// The El-Gamal encryption-only algorithm. - /// - ElGamalEncrypt = 16, - - /// - /// The DSA algorithm. - /// - Dsa = 17, - - /// - /// The elliptic curve algorithm (aka EC or ECDH). - /// - EllipticCurve = 18, - - /// - /// The elliptic curve DSA algorithm (aka ECDSA). - /// - EllipticCurveDsa = 19, - - /// - /// The El-Gamal algorithm. - /// - ElGamalGeneral = 20, - - /// - /// The Diffie-Hellman algorithm. - /// - DiffieHellman = 21, - - /// - /// The Edwards-Curve DSA algorithm (aka EdDSA). - /// - EdwardsCurveDsa = 22 - } -} diff --git a/src/MimeKit/Cryptography/PublicKeyNotFoundException.cs b/src/MimeKit/Cryptography/PublicKeyNotFoundException.cs deleted file mode 100644 index 2221f8f..0000000 --- a/src/MimeKit/Cryptography/PublicKeyNotFoundException.cs +++ /dev/null @@ -1,115 +0,0 @@ -// -// PublicKeyNotFoundException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MimeKit.Cryptography { - /// - /// An exception that is thrown when a public key could not be found for a specified mailbox. - /// - /// - /// An exception that is thrown when a public key could not be found for a specified mailbox. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class PublicKeyNotFoundException : Exception - { -#if SERIALIZABLE - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The serialization info. - /// The stream context. - /// - /// is null. - /// - protected PublicKeyNotFoundException (SerializationInfo info, StreamingContext context) : base (info, context) - { - var text = info.GetString ("Mailbox"); - MailboxAddress mailbox; - - if (MailboxAddress.TryParse (text, out mailbox)) - Mailbox = mailbox; - } -#endif - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The mailbox that could not be resolved to a valid private key. - /// A message explaining the error. - public PublicKeyNotFoundException (MailboxAddress mailbox, string message) : base (message) - { - Mailbox = mailbox; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Sets the - /// with information about the exception. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("Mailbox", Mailbox.ToString (true)); - } -#endif - - /// - /// Gets the key id that could not be found. - /// - /// - /// Gets the key id that could not be found. - /// - /// The key id. - public MailboxAddress Mailbox { - get; private set; - } - } -} diff --git a/src/MimeKit/Cryptography/RsaEncryptionPadding.cs b/src/MimeKit/Cryptography/RsaEncryptionPadding.cs deleted file mode 100644 index e009605..0000000 --- a/src/MimeKit/Cryptography/RsaEncryptionPadding.cs +++ /dev/null @@ -1,251 +0,0 @@ -// -// RsaEncryptionPadding.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -#if NETCOREAPP3_0 -using System.Security.Cryptography; -#endif - -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.X509; - -namespace MimeKit.Cryptography { - /// - /// The RSA encryption padding schemes and parameters used by S/MIME. - /// - /// - /// The RSA encryption padding schemes and parameters used by S/MIME as described in - /// rfc8017. - /// - public sealed class RsaEncryptionPadding : IEquatable - { - /// - /// The PKCS #1 v1.5 encryption padding. - /// - public static readonly RsaEncryptionPadding Pkcs1 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Pkcs1, DigestAlgorithm.None); - - /// - /// The Optimal Asymmetric Encryption Padding (OAEP) scheme using the default (SHA-1) hash algorithm. - /// - public static readonly RsaEncryptionPadding OaepSha1 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Oaep, DigestAlgorithm.Sha1); - - /// - /// The Optimal Asymmetric Encryption Padding (OAEP) scheme using the SHA-256 hash algorithm. - /// - public static readonly RsaEncryptionPadding OaepSha256 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Oaep, DigestAlgorithm.Sha256); - - /// - /// The Optimal Asymmetric Encryption Padding (OAEP) scheme using the SHA-384 hash algorithm. - /// - public static readonly RsaEncryptionPadding OaepSha384 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Oaep, DigestAlgorithm.Sha384); - - /// - /// The Optimal Asymmetric Encryption Padding (OAEP) scheme using the SHA-512 hash algorithm. - /// - public static readonly RsaEncryptionPadding OaepSha512 = new RsaEncryptionPadding (RsaEncryptionPaddingScheme.Oaep, DigestAlgorithm.Sha512); - - RsaEncryptionPadding (RsaEncryptionPaddingScheme scheme, DigestAlgorithm oaepHashAlgorithm) - { - OaepHashAlgorithm = oaepHashAlgorithm; - Scheme = scheme; - } - - /// - /// Get the RSA encryption padding scheme. - /// - /// - /// Gets the RSA encryption padding scheme. - /// - /// The RSA encryption padding scheme. - public RsaEncryptionPaddingScheme Scheme { - get; private set; - } - - /// - /// Get the hash algorithm used for RSAES-OAEP padding. - /// - /// - /// Gets the hash algorithm used for RSAES-OAEP padding. - /// - public DigestAlgorithm OaepHashAlgorithm { - get; private set; - } - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Compares two RSA encryption paddings to determine if they are identical or not. - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public bool Equals (RsaEncryptionPadding other) - { - if (other == null) - return false; - - return other.Scheme == Scheme && other.OaepHashAlgorithm == OaepHashAlgorithm; - } - - /// - /// Determines whether the specified object is equal to the current object. - /// - /// - /// The type of comparison between the current instance and the parameter depends on whether - /// the current instance is a reference type or a value type. - /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals (object obj) - { - return Equals (obj as RsaEncryptionPadding); - } - - /// - /// Returns the hash code for this instance. - /// - /// - /// Returns the hash code for this instance. - /// - /// A hash code for the current object. - public override int GetHashCode () - { - int hash = Scheme.GetHashCode (); - - return ((hash << 5) + hash) ^ OaepHashAlgorithm.GetHashCode (); - } - - /// - /// Returns a that represents the current - /// . - /// - /// - /// Creates a string-representation of the . - /// - /// A that represents the current - /// . - public override string ToString () - { - return Scheme == RsaEncryptionPaddingScheme.Pkcs1 ? "Pkcs1" : "Oaep" + OaepHashAlgorithm.ToString (); - } - - /// - /// Compare two objects for equality. - /// - /// - /// Compares two objects for equality. - /// - /// The first object to compare. - /// The second object to compare. - /// true if and are equal; otherwise, false. - public static bool operator == (RsaEncryptionPadding left, RsaEncryptionPadding right) - { - if (ReferenceEquals (left, null)) - return ReferenceEquals (right, null); - - return left.Equals (right); - } - - /// - /// Compare two objects for inequality. - /// - /// - /// Compares two objects for inequality. - /// - /// The first object to compare. - /// The second object to compare. - /// true if and are unequal; otherwise, false. - public static bool operator != (RsaEncryptionPadding left, RsaEncryptionPadding right) - { - return !(left == right); - } - - /// - /// Create a new using and the specified hash algorithm. - /// - /// - /// Creates a new using and the specified hash algorithm. - /// - /// The hash algorithm. - /// An using and the specified hash algorithm. - /// - /// The is not supported. - /// - public static RsaEncryptionPadding CreateOaep (DigestAlgorithm hashAlgorithm) - { - switch (hashAlgorithm) { - case DigestAlgorithm.Sha1: return OaepSha1; - case DigestAlgorithm.Sha256: return OaepSha256; - case DigestAlgorithm.Sha384: return OaepSha384; - case DigestAlgorithm.Sha512: return OaepSha512; - default: throw new NotSupportedException ($"The {hashAlgorithm} hash algorithm is not supported."); - } - } - - internal RsaesOaepParameters GetRsaesOaepParameters () - { - if (OaepHashAlgorithm == DigestAlgorithm.Sha1) - return new RsaesOaepParameters (); - - var oid = SecureMimeContext.GetDigestOid (OaepHashAlgorithm); - var hashAlgorithm = new AlgorithmIdentifier (new DerObjectIdentifier (oid), DerNull.Instance); - var maskGenFunction = new AlgorithmIdentifier (PkcsObjectIdentifiers.IdMgf1, hashAlgorithm); - - return new RsaesOaepParameters (hashAlgorithm, maskGenFunction, RsaesOaepParameters.DefaultPSourceAlgorithm); - } - - internal AlgorithmIdentifier GetAlgorithmIdentifier () - { - if (Scheme != RsaEncryptionPaddingScheme.Oaep) - return null; - - return new AlgorithmIdentifier (PkcsObjectIdentifiers.IdRsaesOaep, GetRsaesOaepParameters ()); - } - -#if NETCOREAPP3_0 - internal RSAEncryptionPadding AsRSAEncryptionPadding () - { - switch (Scheme) { - case RsaEncryptionPaddingScheme.Oaep: - switch (OaepHashAlgorithm) { - case DigestAlgorithm.Sha1: return RSAEncryptionPadding.OaepSHA1; - case DigestAlgorithm.Sha256: return RSAEncryptionPadding.OaepSHA256; - case DigestAlgorithm.Sha384: return RSAEncryptionPadding.OaepSHA384; - case DigestAlgorithm.Sha512: return RSAEncryptionPadding.OaepSHA512; - default: return null; - } - case RsaEncryptionPaddingScheme.Pkcs1: - return RSAEncryptionPadding.Pkcs1; - default: - return null; - } - } -#endif - } -} diff --git a/src/MimeKit/Cryptography/RsaEncryptionPaddingScheme.cs b/src/MimeKit/Cryptography/RsaEncryptionPaddingScheme.cs deleted file mode 100644 index c867bc3..0000000 --- a/src/MimeKit/Cryptography/RsaEncryptionPaddingScheme.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// RsaEncryptionPaddingScheme.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// The RSA encryption padding schemes used by S/MIME. - /// - /// - /// The RSA encryption padding schemes used by S/MIME as described in - /// rfc8017. - /// - public enum RsaEncryptionPaddingScheme - { - /// - /// The PKCS #1 v1.5 encryption padding scheme. - /// - Pkcs1, - - /// - /// The Optimal Asymmetric Encryption Padding (OAEP) scheme. - /// - Oaep - } -} diff --git a/src/MimeKit/Cryptography/RsaSignaturePadding.cs b/src/MimeKit/Cryptography/RsaSignaturePadding.cs deleted file mode 100644 index dce3132..0000000 --- a/src/MimeKit/Cryptography/RsaSignaturePadding.cs +++ /dev/null @@ -1,153 +0,0 @@ -// -// RsaSignaturePadding.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Cryptography { - /// - /// The RSA signature padding schemes and parameters used by S/MIME. - /// - /// - /// The RSA signature padding schemes and parameters used by S/MIME as described in - /// rfc8017. - /// - public sealed class RsaSignaturePadding : IEquatable - { - /// - /// The PKCS #1 v1.5 signature padding. - /// - public static readonly RsaSignaturePadding Pkcs1 = new RsaSignaturePadding (RsaSignaturePaddingScheme.Pkcs1); - - /// - /// The Probibilistic Signature Scheme (PSS) padding. - /// - public static readonly RsaSignaturePadding Pss = new RsaSignaturePadding (RsaSignaturePaddingScheme.Pss); - - RsaSignaturePadding (RsaSignaturePaddingScheme scheme) - { - Scheme = scheme; - } - - /// - /// Get the RSA signature padding scheme. - /// - /// - /// Gets the RSA signature padding scheme. - /// - /// The RSA signature padding scheme. - public RsaSignaturePaddingScheme Scheme { - get; private set; - } - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Compares two RSA Signature paddings to determine if they are identical or not. - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public bool Equals (RsaSignaturePadding other) - { - if (other == null) - return false; - - return other.Scheme == Scheme; - } - - /// - /// Determines whether the specified object is equal to the current object. - /// - /// - /// The type of comparison between the current instance and the parameter depends on whether - /// the current instance is a reference type or a value type. - /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals (object obj) - { - return Equals (obj as RsaSignaturePadding); - } - - /// - /// Returns the hash code for this instance. - /// - /// - /// Returns the hash code for this instance. - /// - /// A hash code for the current object. - public override int GetHashCode () - { - return Scheme.GetHashCode (); - } - - /// - /// Returns a that represents the current - /// . - /// - /// - /// Creates a string-representation of the . - /// - /// A that represents the current - /// . - public override string ToString () - { - return Scheme == RsaSignaturePaddingScheme.Pkcs1 ? "Pkcs1" : "Pss"; - } - - /// - /// Compare two objects for equality. - /// - /// - /// Compares two objects for equality. - /// - /// The first object to compare. - /// The second object to compare. - /// true if and are equal; otherwise, false. - public static bool operator == (RsaSignaturePadding left, RsaSignaturePadding right) - { - if (ReferenceEquals (left, null)) - return ReferenceEquals (right, null); - - return left.Equals (right); - } - - /// - /// Compare two objects for inequality. - /// - /// - /// Compares two objects for inequality. - /// - /// The first object to compare. - /// The second object to compare. - /// true if and are unequal; otherwise, false. - public static bool operator != (RsaSignaturePadding left, RsaSignaturePadding right) - { - return !(left == right); - } - } -} diff --git a/src/MimeKit/Cryptography/RsaSignaturePaddingScheme.cs b/src/MimeKit/Cryptography/RsaSignaturePaddingScheme.cs deleted file mode 100644 index c408c47..0000000 --- a/src/MimeKit/Cryptography/RsaSignaturePaddingScheme.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// RsaSignaturePaddingScheme.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// The RSA signature padding schemes used by S/MIME. - /// - /// - /// The RSA signature padding schemes used by S/MIME as described in - /// rfc8017. - /// - public enum RsaSignaturePaddingScheme - { - /// - /// The PKCS #1 v1.5 signature padding scheme. - /// - Pkcs1, - - /// - /// The Probibilistic Signature Scheme (PSS). - /// - Pss - } -} diff --git a/src/MimeKit/Cryptography/SecureMailboxAddress.cs b/src/MimeKit/Cryptography/SecureMailboxAddress.cs deleted file mode 100644 index c93bd8d..0000000 --- a/src/MimeKit/Cryptography/SecureMailboxAddress.cs +++ /dev/null @@ -1,219 +0,0 @@ -// -// SecureMailboxAddress.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit.Cryptography { - /// - /// A secure mailbox address which includes a fingerprint for a certificate. - /// - /// - /// When signing or encrypting a message, it is necessary to look up the - /// X.509 certificate in order to do the actual sign or encrypt operation. One - /// way of accomplishing this is to use the email address of sender or recipient - /// as a unique identifier. However, a better approach is to use the fingerprint - /// (or 'thumbprint' in Microsoft parlance) of the user's certificate. - /// - public class SecureMailboxAddress : MailboxAddress - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified fingerprint. - /// - /// The character encoding to be used for encoding the name. - /// The name of the mailbox. - /// The route of the mailbox. - /// The address of the mailbox. - /// The fingerprint of the certificate belonging to the owner of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public SecureMailboxAddress (Encoding encoding, string name, IEnumerable route, string address, string fingerprint) : base (encoding, name, route, address) - { - ValidateFingerprint (fingerprint); - - Fingerprint = fingerprint; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified fingerprint. - /// - /// The name of the mailbox. - /// The route of the mailbox. - /// The address of the mailbox. - /// The fingerprint of the certificate belonging to the owner of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public SecureMailboxAddress (string name, IEnumerable route, string address, string fingerprint) : base (name, route, address) - { - ValidateFingerprint (fingerprint); - - Fingerprint = fingerprint; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified fingerprint. - /// - /// The route of the mailbox. - /// The address of the mailbox. - /// The fingerprint of the certificate belonging to the owner of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use new SecureMailboxAddress (string.Empty, route, address, fingerprint) instead.")] - public SecureMailboxAddress (IEnumerable route, string address, string fingerprint) : base (route, address) - { - ValidateFingerprint (fingerprint); - - Fingerprint = fingerprint; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified fingerprint. - /// - /// The character encoding to be used for encoding the name. - /// The name of the mailbox. - /// The address of the mailbox. - /// The fingerprint of the certificate belonging to the owner of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public SecureMailboxAddress (Encoding encoding, string name, string address, string fingerprint) : base (encoding, name, address) - { - ValidateFingerprint (fingerprint); - - Fingerprint = fingerprint; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified fingerprint. - /// - /// The name of the mailbox. - /// The address of the mailbox. - /// The fingerprint of the certificate belonging to the owner of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// - public SecureMailboxAddress (string name, string address, string fingerprint) : base (name, address) - { - ValidateFingerprint (fingerprint); - - Fingerprint = fingerprint; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified address. - /// - /// The must be in the form user@example.com. - /// This method cannot be used to parse a free-form email address that includes - /// the name or encloses the address in angle brackets. - /// To parse a free-form email address, use - /// instead. - /// - /// - /// The address of the mailbox. - /// The fingerprint of the certificate belonging to the owner of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// - [Obsolete ("Use new SecureMailboxAddress (string.Empty, address, fingerprint) instead.")] - public SecureMailboxAddress (string address, string fingerprint) : base (address) - { - ValidateFingerprint (fingerprint); - - Fingerprint = fingerprint; - } - - static void ValidateFingerprint (string fingerprint) - { - if (fingerprint == null) - throw new ArgumentNullException (nameof (fingerprint)); - - for (int i = 0; i < fingerprint.Length; i++) { - if (fingerprint[i] > 128 || !((byte) fingerprint[i]).IsXDigit ()) - throw new ArgumentException ("The fingerprint should be a hex-encoded string.", nameof (fingerprint)); - } - } - - /// - /// Gets the fingerprint of the certificate and/or key to use for signing or encrypting. - /// - /// - /// A fingerprint is a SHA-1 hash of the raw certificate data and is often used - /// as a unique identifier for a particular certificate in a certificate store. - /// - /// - /// - /// The fingerprint of the certificate. - public string Fingerprint { - get; private set; - } - } -} diff --git a/src/MimeKit/Cryptography/SecureMimeContext.cs b/src/MimeKit/Cryptography/SecureMimeContext.cs deleted file mode 100644 index 2aa2978..0000000 --- a/src/MimeKit/Cryptography/SecureMimeContext.cs +++ /dev/null @@ -1,842 +0,0 @@ -// -// SecureMimeContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; - -using Org.BouncyCastle.Cms; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Asn1.Ntt; -using Org.BouncyCastle.Asn1.Kisa; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.Smime; - -namespace MimeKit.Cryptography { - /// - /// A Secure MIME (S/MIME) cryptography context. - /// - /// - /// Generally speaking, applications should not use a - /// directly, but rather via higher level APIs such as - /// and . - /// - public abstract class SecureMimeContext : CryptographyContext - { - static readonly string[] ProtocolSubtypes = { "pkcs7-signature", "pkcs7-mime", "pkcs7-keys", "x-pkcs7-signature", "x-pkcs7-mime", "x-pkcs7-keys" }; - internal const X509KeyUsageFlags DigitalSignatureKeyUsageFlags = X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation; - internal static readonly int EncryptionAlgorithmCount = Enum.GetValues (typeof (EncryptionAlgorithm)).Length; - internal static readonly DerObjectIdentifier Blowfish = new DerObjectIdentifier ("1.3.6.1.4.1.3029.1.2"); - internal static readonly DerObjectIdentifier Twofish = new DerObjectIdentifier ("1.3.6.1.4.1.25258.3.3"); - - /// - /// Initialize a new instance of the class. - /// - /// - /// Enables the following encryption algorithms by default: - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - protected SecureMimeContext () - { - EncryptionAlgorithmRank = new[] { - EncryptionAlgorithm.Aes256, - EncryptionAlgorithm.Aes192, - EncryptionAlgorithm.Aes128, - //EncryptionAlgorithm.Twofish, - EncryptionAlgorithm.Seed, - EncryptionAlgorithm.Camellia256, - EncryptionAlgorithm.Camellia192, - EncryptionAlgorithm.Camellia128, - EncryptionAlgorithm.Cast5, - EncryptionAlgorithm.Blowfish, - EncryptionAlgorithm.TripleDes, - EncryptionAlgorithm.Idea, - EncryptionAlgorithm.RC2128, - EncryptionAlgorithm.RC264, - EncryptionAlgorithm.Des, - EncryptionAlgorithm.RC240 - }; - - foreach (var algorithm in EncryptionAlgorithmRank) { - Enable (algorithm); - - // Don't enable anything weaker than Triple-DES by default - if (algorithm == EncryptionAlgorithm.TripleDes) - break; - } - - // Disable Blowfish and Twofish by default for now - Disable (EncryptionAlgorithm.Blowfish); - Disable (EncryptionAlgorithm.Twofish); - - // TODO: Set a preferred digest algorithm rank and enable them. - } - - /// - /// Get the signature protocol. - /// - /// - /// The signature protocol is used by - /// in order to determine what the protocol parameter of the Content-Type - /// header should be. - /// - /// The signature protocol. - public override string SignatureProtocol { - get { return "application/pkcs7-signature"; } - } - - /// - /// Get the encryption protocol. - /// - /// - /// The encryption protocol is used by - /// in order to determine what the protocol parameter of the Content-Type - /// header should be. - /// - /// The encryption protocol. - public override string EncryptionProtocol { - get { return "application/pkcs7-mime"; } - } - - /// - /// Get the key exchange protocol. - /// - /// - /// Gets the key exchange protocol. - /// - /// The key exchange protocol. - public override string KeyExchangeProtocol { - get { return "application/pkcs7-mime"; } - } - - /// - /// Check whether or not the specified protocol is supported by the . - /// - /// - /// Used in order to make sure that the protocol parameter value specified in either a multipart/signed - /// or multipart/encrypted part is supported by the supplied cryptography context. - /// - /// true if the protocol is supported; otherwise false - /// The protocol. - /// - /// is null. - /// - public override bool Supports (string protocol) - { - if (protocol == null) - throw new ArgumentNullException (nameof (protocol)); - - if (!protocol.StartsWith ("application/", StringComparison.OrdinalIgnoreCase)) - return false; - - int startIndex = "application/".Length; - int subtypeLength = protocol.Length - startIndex; - - for (int i = 0; i < ProtocolSubtypes.Length; i++) { - if (subtypeLength != ProtocolSubtypes[i].Length) - continue; - - if (string.Compare (protocol, startIndex, ProtocolSubtypes[i], 0, subtypeLength, StringComparison.OrdinalIgnoreCase) == 0) - return true; - } - - return false; - } - - /// - /// Get the string name of the digest algorithm for use with the micalg parameter of a multipart/signed part. - /// - /// - /// Maps the to the appropriate string identifier - /// as used by the micalg parameter value of a multipart/signed Content-Type - /// header. For example: - /// - /// AlgorithmName - /// md2 - /// md4 - /// md5 - /// sha-1 - /// sha-224 - /// sha-256 - /// sha-384 - /// sha-512 - /// tiger-192 - /// ripemd160 - /// haval-5-160 - /// - /// - /// The micalg value. - /// The digest algorithm. - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - public override string GetDigestAlgorithmName (DigestAlgorithm micalg) - { - switch (micalg) { - case DigestAlgorithm.MD5: return "md5"; - case DigestAlgorithm.Sha1: return "sha-1"; - case DigestAlgorithm.RipeMD160: return "ripemd160"; - case DigestAlgorithm.MD2: return "md2"; - case DigestAlgorithm.Tiger192: return "tiger192"; - case DigestAlgorithm.Haval5160: return "haval-5-160"; - case DigestAlgorithm.Sha256: return "sha-256"; - case DigestAlgorithm.Sha384: return "sha-384"; - case DigestAlgorithm.Sha512: return "sha-512"; - case DigestAlgorithm.Sha224: return "sha-224"; - case DigestAlgorithm.MD4: return "md4"; - case DigestAlgorithm.DoubleSha: - throw new NotSupportedException (string.Format ("{0} is not supported.", micalg)); - default: - throw new ArgumentOutOfRangeException (nameof (micalg), micalg, string.Format ("Unknown DigestAlgorithm: {0}", micalg)); - } - } - - /// - /// Get the digest algorithm from the micalg parameter value in a multipart/signed part. - /// - /// - /// Maps the micalg parameter value string back to the appropriate . - /// Maps the micalg parameter value string back to the appropriate - /// - /// AlgorithmName - /// md2 - /// md4 - /// md5 - /// sha-1 - /// sha-224 - /// sha-256 - /// sha-384 - /// sha-512 - /// tiger-192 - /// ripemd160 - /// haval-5-160 - /// - /// - /// The digest algorithm. - /// The micalg parameter value. - /// - /// is null. - /// - public override DigestAlgorithm GetDigestAlgorithm (string micalg) - { - if (micalg == null) - throw new ArgumentNullException (nameof (micalg)); - - switch (micalg.ToLowerInvariant ()) { - case "md5": return DigestAlgorithm.MD5; - case "sha-1": return DigestAlgorithm.Sha1; - case "ripemd160": return DigestAlgorithm.RipeMD160; - case "md2": return DigestAlgorithm.MD2; - case "tiger192": return DigestAlgorithm.Tiger192; - case "haval-5-160": return DigestAlgorithm.Haval5160; - case "sha-256": return DigestAlgorithm.Sha256; - case "sha-384": return DigestAlgorithm.Sha384; - case "sha-512": return DigestAlgorithm.Sha512; - case "sha-224": return DigestAlgorithm.Sha224; - case "md4": return DigestAlgorithm.MD4; - default: return DigestAlgorithm.None; - } - } - - /// - /// Get the OID for the digest algorithm. - /// - /// - /// Gets the OID for the digest algorithm. - /// - /// The digest oid. - /// The digest algorithm. - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - internal protected static string GetDigestOid (DigestAlgorithm digestAlgo) - { - switch (digestAlgo) { - case DigestAlgorithm.MD5: return CmsSignedGenerator.DigestMD5; - case DigestAlgorithm.Sha1: return CmsSignedGenerator.DigestSha1; - case DigestAlgorithm.MD2: return PkcsObjectIdentifiers.MD2.Id; - case DigestAlgorithm.Sha256: return CmsSignedGenerator.DigestSha256; - case DigestAlgorithm.Sha384: return CmsSignedGenerator.DigestSha384; - case DigestAlgorithm.Sha512: return CmsSignedGenerator.DigestSha512; - case DigestAlgorithm.Sha224: return CmsSignedGenerator.DigestSha224; - case DigestAlgorithm.MD4: return PkcsObjectIdentifiers.MD4.Id; - case DigestAlgorithm.RipeMD160: return CmsSignedGenerator.DigestRipeMD160; - case DigestAlgorithm.DoubleSha: - case DigestAlgorithm.Tiger192: - case DigestAlgorithm.Haval5160: - throw new NotSupportedException (string.Format ("{0} is not supported.", digestAlgo)); - default: - throw new ArgumentOutOfRangeException (nameof (digestAlgo), digestAlgo, string.Format ("Unknown DigestAlgorithm: {0}", digestAlgo)); - } - } - - internal static bool TryGetDigestAlgorithm (string id, out DigestAlgorithm algorithm) - { - if (id == CmsSignedGenerator.DigestSha1) { - algorithm = DigestAlgorithm.Sha1; - return true; - } - - if (id == CmsSignedGenerator.DigestSha224) { - algorithm = DigestAlgorithm.Sha224; - return true; - } - - if (id == CmsSignedGenerator.DigestSha256) { - algorithm = DigestAlgorithm.Sha256; - return true; - } - - if (id == CmsSignedGenerator.DigestSha384) { - algorithm = DigestAlgorithm.Sha384; - return true; - } - - if (id == CmsSignedGenerator.DigestSha512) { - algorithm = DigestAlgorithm.Sha512; - return true; - } - - if (id == CmsSignedGenerator.DigestRipeMD160) { - algorithm = DigestAlgorithm.RipeMD160; - return true; - } - - if (id == CmsSignedGenerator.DigestMD5) { - algorithm = DigestAlgorithm.MD5; - return true; - } - - if (id == PkcsObjectIdentifiers.MD4.Id) { - algorithm = DigestAlgorithm.MD4; - return true; - } - - if (id == PkcsObjectIdentifiers.MD2.Id) { - algorithm = DigestAlgorithm.MD2; - return true; - } - - algorithm = DigestAlgorithm.None; - - return false; - } - - //class VoteComparer : IComparer - //{ - // public int Compare (int x, int y) - // { - // return y - x; - // } - //} - - /// - /// Get the preferred encryption algorithm to use for encrypting to the specified recipients. - /// - /// - /// Gets the preferred encryption algorithm to use for encrypting to the specified recipients - /// based on the encryption algorithms supported by each of the recipients, the - /// , and the - /// . - /// If the supported encryption algorithms are unknown for any recipient, it is assumed that - /// the recipient supports at least the Triple-DES encryption algorithm. - /// - /// The preferred encryption algorithm. - /// The recipients. - protected virtual EncryptionAlgorithm GetPreferredEncryptionAlgorithm (CmsRecipientCollection recipients) - { - var votes = new int[EncryptionAlgorithmCount]; - int need = recipients.Count; - - foreach (var recipient in recipients) { - int cast = EncryptionAlgorithmCount; - - foreach (var algorithm in recipient.EncryptionAlgorithms) - votes[(int) algorithm]++; - } - - // Starting with S/MIME v3 (published in 1999), Triple-DES is a REQUIRED algorithm. - // S/MIME v2.x and older only required RC2/40, but SUGGESTED Triple-DES. - // Considering the fact that Bruce Schneier was able to write a - // screensaver that could crack RC2/40 back in the late 90's, let's - // not default to anything weaker than Triple-DES... - EncryptionAlgorithm chosen = EncryptionAlgorithm.TripleDes; - int nvotes = 0; - - votes[(int) EncryptionAlgorithm.TripleDes] = need; - - // iterate through the algorithms, from strongest to weakest, keeping track - // of the algorithm with the most amount of votes (between algorithms with - // the same number of votes, choose the strongest of the 2 - i.e. the one - // that we arrive at first). - var algorithms = EncryptionAlgorithmRank; - for (int i = 0; i < algorithms.Length; i++) { - var algorithm = algorithms[i]; - - if (!IsEnabled (algorithm)) - continue; - - if (votes[(int) algorithm] > nvotes) { - nvotes = votes[(int) algorithm]; - chosen = algorithm; - } - } - - return chosen; - } - - /// - /// Compresses the specified stream. - /// - /// - /// Compresses the specified stream. - /// - /// A new instance - /// containing the compressed content. - /// The stream to compress. - /// - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public ApplicationPkcs7Mime Compress (Stream stream) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var compresser = new CmsCompressedDataGenerator (); - var processable = new CmsProcessableInputStream (stream); - var compressed = compresser.Generate (processable, CmsCompressedDataGenerator.ZLib); - var encoded = compressed.GetEncoded (); - - return new ApplicationPkcs7Mime (SecureMimeType.CompressedData, new MemoryStream (encoded, false)); - } - - /// - /// Decompress the specified stream. - /// - /// - /// Decompress the specified stream. - /// - /// The decompressed mime part. - /// The stream to decompress. - /// - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public MimeEntity Decompress (Stream stream) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new CmsCompressedDataParser (stream); - var content = parser.GetContent (); - - return MimeEntity.Load (content.ContentStream); - } - - /// - /// Decompress the specified stream to an output stream. - /// - /// - /// Decompress the specified stream to an output stream. - /// - /// The stream to decompress. - /// The output stream. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public virtual void DecompressTo (Stream stream, Stream output) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - var parser = new CmsCompressedDataParser (stream); - var content = parser.GetContent (); - - content.ContentStream.CopyTo (output, 4096); - } - - internal SmimeCapabilitiesAttribute GetSecureMimeCapabilitiesAttribute (bool includeRsaesOaep) - { - var capabilities = new SmimeCapabilityVector (); - - foreach (var algorithm in EncryptionAlgorithmRank) { - if (!IsEnabled (algorithm)) - continue; - - switch (algorithm) { - case EncryptionAlgorithm.Aes128: - capabilities.AddCapability (SmimeCapabilities.Aes128Cbc); - break; - case EncryptionAlgorithm.Aes192: - capabilities.AddCapability (SmimeCapabilities.Aes192Cbc); - break; - case EncryptionAlgorithm.Aes256: - capabilities.AddCapability (SmimeCapabilities.Aes256Cbc); - break; - case EncryptionAlgorithm.Blowfish: - capabilities.AddCapability (Blowfish); - break; - case EncryptionAlgorithm.Camellia128: - capabilities.AddCapability (NttObjectIdentifiers.IdCamellia128Cbc); - break; - case EncryptionAlgorithm.Camellia192: - capabilities.AddCapability (NttObjectIdentifiers.IdCamellia192Cbc); - break; - case EncryptionAlgorithm.Camellia256: - capabilities.AddCapability (NttObjectIdentifiers.IdCamellia256Cbc); - break; - case EncryptionAlgorithm.Cast5: - capabilities.AddCapability (SmimeCapabilities.Cast5Cbc); - break; - case EncryptionAlgorithm.Des: - capabilities.AddCapability (SmimeCapabilities.DesCbc); - break; - case EncryptionAlgorithm.Idea: - capabilities.AddCapability (SmimeCapabilities.IdeaCbc); - break; - case EncryptionAlgorithm.RC240: - capabilities.AddCapability (SmimeCapabilities.RC2Cbc, 40); - break; - case EncryptionAlgorithm.RC264: - capabilities.AddCapability (SmimeCapabilities.RC2Cbc, 64); - break; - case EncryptionAlgorithm.RC2128: - capabilities.AddCapability (SmimeCapabilities.RC2Cbc, 128); - break; - case EncryptionAlgorithm.Seed: - capabilities.AddCapability (KisaObjectIdentifiers.IdSeedCbc); - break; - case EncryptionAlgorithm.TripleDes: - capabilities.AddCapability (SmimeCapabilities.DesEde3Cbc); - break; - //case EncryptionAlgorithm.Twofish: - // capabilities.AddCapability (Twofish); - // break; - } - } - - if (includeRsaesOaep) { - capabilities.AddCapability (PkcsObjectIdentifiers.IdRsaesOaep, RsaEncryptionPadding.OaepSha1.GetRsaesOaepParameters ()); - capabilities.AddCapability (PkcsObjectIdentifiers.IdRsaesOaep, RsaEncryptionPadding.OaepSha256.GetRsaesOaepParameters ()); - capabilities.AddCapability (PkcsObjectIdentifiers.IdRsaesOaep, RsaEncryptionPadding.OaepSha384.GetRsaesOaepParameters ()); - capabilities.AddCapability (PkcsObjectIdentifiers.IdRsaesOaep, RsaEncryptionPadding.OaepSha512.GetRsaesOaepParameters ()); - } - - return new SmimeCapabilitiesAttribute (capabilities); - } - - /// - /// Cryptographically signs and encapsulates the content using the specified signer. - /// - /// - /// Cryptographically signs and encapsulates the content using the specified signer. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - public abstract ApplicationPkcs7Mime EncapsulatedSign (CmsSigner signer, Stream content); - - /// - /// Cryptographically signs and encapsulates the content using the specified signer and digest algorithm. - /// - /// - /// Cryptographically signs and encapsulates the content using the specified signer and digest algorithm. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - /// - /// A signing certificate could not be found for . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public abstract ApplicationPkcs7Mime EncapsulatedSign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content); - - /// - /// Cryptographically signs the content using the specified signer. - /// - /// - /// Cryptographically signs the content using the specified signer. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - public abstract ApplicationPkcs7Signature Sign (CmsSigner signer, Stream content); - - /// - /// Verify the digital signatures of the specified signed data and extract the original content. - /// - /// - /// Verifies the digital signatures of the specified signed data and extracts the original content. - /// - /// The list of digital signatures. - /// The signed data. - /// The extracted MIME entity. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The extracted content could not be parsed as a MIME entity. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public abstract DigitalSignatureCollection Verify (Stream signedData, out MimeEntity entity, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Verify the digital signatures of the specified signed data and extract the original content. - /// - /// - /// Verifies the digital signatures of the specified signed data and extracts the original content. - /// - /// The extracted content stream. - /// The signed data. - /// The digital signatures. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public abstract Stream Verify (Stream signedData, out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Encrypts the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted content. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - public abstract ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, Stream content); - - /// - /// Decrypts the specified encryptedData to an output stream. - /// - /// - /// Decrypts the specified encryptedData to an output stream. - /// - /// The encrypted data. - /// The stream to write the decrypted data to. - /// - /// is null. - /// -or- - /// is null. - /// - public abstract void DecryptTo (Stream encryptedData, Stream decryptedData); - - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// The raw certificate and key data. - /// The password to unlock the stream. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// Importing keys is not supported by this cryptography context. - /// - public abstract void Import (Stream stream, string password); - - /// - /// Imports certificates and keys from a pkcs12 file. - /// - /// - /// Imports certificates and keys from a pkcs12 file. - /// - /// The raw certificate and key data in pkcs12 format. - /// The password to unlock the stream. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// -or- - /// does not contain a private key. - /// -or- - /// does not contain a certificate that could be used for signing. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An I/O error occurred. - /// - /// - /// Importing keys is not supported by this cryptography context. - /// - public virtual void Import (string fileName, string password) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - using (var stream = File.OpenRead (fileName)) - Import (stream, password); - } - - /// - /// Imports the specified certificate. - /// - /// - /// Imports the specified certificate. - /// - /// The certificate. - /// - /// is null. - /// - public abstract void Import (X509Certificate certificate); - - /// - /// Imports the specified certificate revocation list. - /// - /// - /// Imports the specified certificate revocation list. - /// - /// The certificate revocation list. - /// - /// is null. - /// - public abstract void Import (X509Crl crl); - - /// - /// Imports certificates (as from a certs-only application/pkcs-mime part) - /// from the specified stream. - /// - /// - /// Imports certificates (as from a certs-only application/pkcs-mime part) - /// from the specified stream. - /// - /// The raw key data. - /// - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override void Import (Stream stream) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new CmsSignedDataParser (stream); - var certificates = parser.GetCertificates ("Collection"); - - foreach (X509Certificate certificate in certificates.GetMatches (null)) - Import (certificate); - - var crls = parser.GetCrls ("Collection"); - - foreach (X509Crl crl in crls.GetMatches (null)) - Import (crl); - } - } -} diff --git a/src/MimeKit/Cryptography/SecureMimeDigitalCertificate.cs b/src/MimeKit/Cryptography/SecureMimeDigitalCertificate.cs deleted file mode 100644 index 073460a..0000000 --- a/src/MimeKit/Cryptography/SecureMimeDigitalCertificate.cs +++ /dev/null @@ -1,149 +0,0 @@ -// -// SecureMimeDigitalCertificate.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using Org.BouncyCastle.X509; - -namespace MimeKit.Cryptography { - /// - /// An S/MIME digital certificate. - /// - /// - /// An S/MIME digital certificate. - /// - public class SecureMimeDigitalCertificate : IDigitalCertificate - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// An X.509 certificate. - /// - /// is null. - /// - public SecureMimeDigitalCertificate (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - Certificate = certificate; - Fingerprint = certificate.GetFingerprint (); - PublicKeyAlgorithm = certificate.GetPublicKeyAlgorithm (); - } - - /// - /// Get the X.509 certificate. - /// - /// - /// Gets the X.509 certificate. - /// - /// The certificate. - public X509Certificate Certificate { - get; private set; - } - -// /// -// /// Gets the chain status. -// /// -// /// The chain status. -// public X509ChainStatusFlags ChainStatus { -// get; internal set; -// } - - #region IDigitalCertificate implementation - - /// - /// Gets the public key algorithm supported by the certificate. - /// - /// - /// Gets the public key algorithm supported by the certificate. - /// - /// The public key algorithm. - public PublicKeyAlgorithm PublicKeyAlgorithm { - get; private set; - } - - /// - /// Gets the date that the certificate was created. - /// - /// - /// Gets the date that the certificate was created. - /// - /// The creation date. - public DateTime CreationDate { - get { return Certificate.NotBefore.ToUniversalTime (); } - } - - /// - /// Gets the expiration date of the certificate. - /// - /// - /// Gets the expiration date of the certificate. - /// - /// The expiration date. - public DateTime ExpirationDate { - get { return Certificate.NotAfter.ToUniversalTime (); } - } - - /// - /// Gets the fingerprint of the certificate. - /// - /// - /// Gets the fingerprint of the certificate. - /// - /// The fingerprint. - public string Fingerprint { - get; private set; - } - - /// - /// Gets the email address of the owner of the certificate. - /// - /// - /// Gets the email address of the owner of the certificate. - /// - /// The email address. - public string Email { - get { return Certificate.GetSubjectEmailAddress (); } - } - - /// - /// Gets the name of the owner of the certificate. - /// - /// - /// Gets the name of the owner of the certificate. - /// - /// The name of the owner. - public string Name { - get { return Certificate.GetCommonName (); } - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/SecureMimeDigitalSignature.cs b/src/MimeKit/Cryptography/SecureMimeDigitalSignature.cs deleted file mode 100644 index 61c3f8c..0000000 --- a/src/MimeKit/Cryptography/SecureMimeDigitalSignature.cs +++ /dev/null @@ -1,265 +0,0 @@ -// -// SecureMimeDigitalSignature.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -using Org.BouncyCastle.Cms; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Pkix; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Cms; -using Org.BouncyCastle.Asn1.Smime; -using Org.BouncyCastle.Asn1.X509; - -using MimeKit.Utils; - -namespace MimeKit.Cryptography { - /// - /// An S/MIME digital signature. - /// - /// - /// An S/MIME digital signature. - /// - public class SecureMimeDigitalSignature : IDigitalSignature - { - DigitalSignatureVerifyException vex; - bool? valid; - - static DateTime ToAdjustedDateTime (DerUtcTime time) - { - //try { - // return time.ToAdjustedDateTime (); - //} catch { - return DateUtils.Parse (time.AdjustedTimeString, "yyyyMMddHHmmsszzz"); - //} - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The information about the signer. - /// The signer's certificate. - /// - /// is null. - /// - public SecureMimeDigitalSignature (SignerInformation signerInfo, X509Certificate certificate) - { - if (signerInfo == null) - throw new ArgumentNullException (nameof (signerInfo)); - - SignerInfo = signerInfo; - - var algorithms = new List (); - DigestAlgorithm digestAlgo; - - if (signerInfo.SignedAttributes != null) { - Asn1EncodableVector vector = signerInfo.SignedAttributes.GetAll (CmsAttributes.SigningTime); - foreach (Org.BouncyCastle.Asn1.Cms.Attribute attr in vector) { - var signingTime = (DerUtcTime) ((DerSet) attr.AttrValues)[0]; - CreationDate = ToAdjustedDateTime (signingTime); - break; - } - - vector = signerInfo.SignedAttributes.GetAll (SmimeAttributes.SmimeCapabilities); - foreach (Org.BouncyCastle.Asn1.Cms.Attribute attr in vector) { - foreach (Asn1Sequence sequence in attr.AttrValues) { - for (int i = 0; i < sequence.Count; i++) { - var identifier = AlgorithmIdentifier.GetInstance (sequence[i]); - EncryptionAlgorithm algorithm; - - if (BouncyCastleSecureMimeContext.TryGetEncryptionAlgorithm (identifier, out algorithm)) - algorithms.Add (algorithm); - } - } - } - } - - EncryptionAlgorithms = algorithms.ToArray (); - - if (BouncyCastleSecureMimeContext.TryGetDigestAlgorithm (signerInfo.DigestAlgorithmID, out digestAlgo)) - DigestAlgorithm = digestAlgo; - - if (certificate != null) - SignerCertificate = new SecureMimeDigitalCertificate (certificate); - } - - /// - /// Gets the signer info. - /// - /// - /// Gets the signer info. - /// - /// The signer info. - public SignerInformation SignerInfo { - get; private set; - } - - /// - /// Gets the list of encryption algorithms, in preferential order, - /// that the signer's client supports. - /// - /// - /// Gets the list of encryption algorithms, in preferential order, - /// that the signer's client supports. - /// - /// The S/MIME encryption algorithms. - public EncryptionAlgorithm[] EncryptionAlgorithms { - get; private set; - } - - /// - /// Gets the certificate chain. - /// - /// - /// If building the certificate chain failed, this value will be null and - /// will be set. - /// - /// The certificate chain. - public PkixCertPath Chain { - get; internal set; - } - - /// - /// The exception that occurred, if any, while building the certificate chain. - /// - /// - /// This will only be set if building the certificate chain failed. - /// - /// The exception. - public Exception ChainException { - get; internal set; - } - - #region IDigitalSignature implementation - - /// - /// Gets certificate used by the signer. - /// - /// - /// Gets certificate used by the signer. - /// - /// The signer's certificate. - public IDigitalCertificate SignerCertificate { - get; private set; - } - - /// - /// Gets the public key algorithm used for the signature. - /// - /// - /// Gets the public key algorithm used for the signature. - /// - /// The public key algorithm. - public PublicKeyAlgorithm PublicKeyAlgorithm { - get { return SignerCertificate != null ? SignerCertificate.PublicKeyAlgorithm : PublicKeyAlgorithm.None; } - } - - /// - /// Gets the digest algorithm used for the signature. - /// - /// - /// Gets the digest algorithm used for the signature. - /// - /// The digest algorithm. - public DigestAlgorithm DigestAlgorithm { - get; private set; - } - - /// - /// Gets the creation date of the digital signature. - /// - /// - /// Gets the creation date of the digital signature. - /// - /// The creation date in coordinated universal time (UTC). - public DateTime CreationDate { - get; private set; - } - - /// - /// Verifies the digital signature. - /// - /// - /// Verifies the digital signature. - /// - /// true if the signature is valid; otherwise, false. - /// - /// An error verifying the signature has occurred. - /// - public bool Verify () - { - return Verify (false); - } - - /// - /// Verifies the digital signature. - /// - /// - /// Verifies the digital signature. - /// - /// true if only the signature itself should be verified; otherwise, both the signature and the certificate chain are validated. - /// true if the signature is valid; otherwise, false. - /// - /// An error verifying the signature has occurred. - /// - public bool Verify (bool verifySignatureOnly) - { - if (vex != null) - throw vex; - - if (SignerCertificate == null) { - var message = string.Format ("Failed to verify digital signature: missing certificate."); - vex = new DigitalSignatureVerifyException (message); - throw vex; - } - - if (!valid.HasValue) { - try { - var certificate = ((SecureMimeDigitalCertificate) SignerCertificate).Certificate; - valid = SignerInfo.Verify (certificate); - } catch (Exception ex) { - var message = string.Format ("Failed to verify digital signature: {0}", ex.Message); - vex = new DigitalSignatureVerifyException (message, ex); - throw vex; - } - } - - if (!verifySignatureOnly && ChainException != null) { - var message = string.Format ("Failed to verify digital signature chain: {0}", ChainException.Message); - - throw new DigitalSignatureVerifyException (message, ChainException); - } - - return valid.Value; - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/SecureMimeType.cs b/src/MimeKit/Cryptography/SecureMimeType.cs deleted file mode 100644 index 150e002..0000000 --- a/src/MimeKit/Cryptography/SecureMimeType.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -// SecureMimeType.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// The type of S/MIME data that an application/pkcs7-mime part contains. - /// - /// - /// The type of S/MIME data that an application/pkcs7-mime part contains. - /// - public enum SecureMimeType { - /// - /// The S/MIME data type is unknown. - /// - Unknown = -1, - - /// - /// The S/MIME content is compressed. - /// - CompressedData, - - /// - /// The S/MIME content is encrypted. - /// - EnvelopedData, - - /// - /// The S/MIME content is signed. - /// - SignedData, - - /// - /// The S/MIME content contains only certificates. - /// - CertsOnly, - - /// - /// The S/MIME content is both signed and encrypted. - /// - AuthEnvelopedData, - } -} diff --git a/src/MimeKit/Cryptography/SqlCertificateDatabase.cs b/src/MimeKit/Cryptography/SqlCertificateDatabase.cs deleted file mode 100644 index 4bdaada..0000000 --- a/src/MimeKit/Cryptography/SqlCertificateDatabase.cs +++ /dev/null @@ -1,832 +0,0 @@ -// -// SqlCertificateDatabase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Data; -using System.Text; -using System.Data.Common; -using System.Collections.Generic; - -#if __MOBILE__ -using Mono.Data.Sqlite; -#else -using System.Reflection; -#endif - -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.X509.Store; - -namespace MimeKit.Cryptography { - /// - /// An abstract X.509 certificate database built on generic SQL storage. - /// - /// - /// An X.509 certificate database is used for storing certificates, metdata related to the certificates - /// (such as encryption algorithms supported by the associated client), certificate revocation lists (CRLs), - /// and private keys. - /// This particular database uses SQLite to store the data. - /// - public abstract class SqlCertificateDatabase : X509CertificateDatabase - { - readonly DataTable certificatesTable, crlsTable; - readonly DbConnection connection; - bool disposed; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new using the provided database connection. - /// - /// The database . - /// The password used for encrypting and decrypting the private keys. - /// - /// is null. - /// -or- - /// is null. - /// - protected SqlCertificateDatabase (DbConnection connection, string password) : base (password) - { - if (connection == null) - throw new ArgumentNullException (nameof (connection)); - - this.connection = connection; - - if (connection.State != ConnectionState.Open) - connection.Open (); - - certificatesTable = CreateCertificatesDataTable ("CERTIFICATES"); - crlsTable = CreateCrlsDataTable ("CRLS"); - - CreateCertificatesTable (certificatesTable); - CreateCrlsTable (crlsTable); - } - -#if NETSTANDARD1_3 || NETSTANDARD1_6 -#pragma warning disable 1591 - protected class DataColumn - { - public DataColumn (string columnName, Type dataType) - { - ColumnName = columnName; - DataType = dataType; - } - - public DataColumn () - { - } - - public bool AllowDBNull { - get; set; - } - - public bool AutoIncrement { - get; set; - } - - public string ColumnName { - get; set; - } - - public Type DataType { - get; set; - } - - public bool Unique { - get; set; - } - } - - protected class DataColumnCollection : List - { - public int IndexOf (string columnName) - { - for (int i = 0; i < Count; i++) { - if (this[i].ColumnName.Equals (columnName, StringComparison.Ordinal)) - return i; - } - - return -1; - } - } - - protected class DataTable - { - public DataTable (string tableName) - { - Columns = new DataColumnCollection (); - TableName = tableName; - } - - public string TableName { - get; set; - } - - public DataColumnCollection Columns { - get; private set; - } - - public DataColumn[] PrimaryKey { - get; set; - } - } -#pragma warning restore 1591 -#endif - - static DataTable CreateCertificatesDataTable (string tableName) - { - var table = new DataTable (tableName); - table.Columns.Add (new DataColumn ("ID", typeof (int)) { AutoIncrement = true }); - table.Columns.Add (new DataColumn ("TRUSTED", typeof (bool)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("ANCHOR", typeof (bool)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("BASICCONSTRAINTS", typeof (int)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("KEYUSAGE", typeof (int)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("NOTBEFORE", typeof (long)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("NOTAFTER", typeof (long)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("ISSUERNAME", typeof (string)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("SERIALNUMBER", typeof (string)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("SUBJECTNAME", typeof (string)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("SUBJECTKEYIDENTIFIER", typeof (string)) { AllowDBNull = true }); - table.Columns.Add (new DataColumn ("SUBJECTEMAIL", typeof (string)) { AllowDBNull = true }); - table.Columns.Add (new DataColumn ("FINGERPRINT", typeof (string)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("ALGORITHMS", typeof (string)) { AllowDBNull = true }); - table.Columns.Add (new DataColumn ("ALGORITHMSUPDATED", typeof (long)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("CERTIFICATE", typeof (byte[])) { AllowDBNull = false, Unique = true }); - table.Columns.Add (new DataColumn ("PRIVATEKEY", typeof (byte[])) { AllowDBNull = true }); - table.PrimaryKey = new DataColumn[] { table.Columns[0] }; - - return table; - } - - static DataTable CreateCrlsDataTable (string tableName) - { - var table = new DataTable (tableName); - table.Columns.Add (new DataColumn ("ID", typeof (int)) { AutoIncrement = true }); - table.Columns.Add (new DataColumn ("DELTA", typeof (bool)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("ISSUERNAME", typeof (string)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("THISUPDATE", typeof (long)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("NEXTUPDATE", typeof (long)) { AllowDBNull = false }); - table.Columns.Add (new DataColumn ("CRL", typeof (byte[])) { AllowDBNull = false }); - table.PrimaryKey = new DataColumn[] { table.Columns[0] }; - - return table; - } - - /// - /// Gets the columns for the specified table. - /// - /// - /// Gets the list of columns for the specified table. - /// - /// The . - /// The name of the table. - /// The list of columns. - protected abstract IList GetTableColumns (DbConnection connection, string tableName); - - /// - /// Gets the command to create a table. - /// - /// - /// Constructs the command to create a table. - /// - /// The . - /// The table. - protected abstract void CreateTable (DbConnection connection, DataTable table); - - /// - /// Adds a column to a table. - /// - /// - /// Adds a column to a table. - /// - /// The . - /// The table. - /// The column to add. - protected abstract void AddTableColumn (DbConnection connection, DataTable table, DataColumn column); - - static string GetIndexName (string tableName, string[] columnNames) - { - return string.Format ("{0}_{1}_INDEX", tableName, string.Join ("_", columnNames)); - } - - static void CreateIndex (DbConnection connection, string tableName, string[] columnNames) - { - var indexName = GetIndexName (tableName, columnNames); - var query = string.Format ("CREATE INDEX IF NOT EXISTS {0} ON {1}({2})", indexName, tableName, string.Join (", ", columnNames)); - - using (var command = connection.CreateCommand ()) { - command.CommandText = query; - command.ExecuteNonQuery (); - } - } - - static void RemoveIndex (DbConnection connection, string tableName, string[] columnNames) - { - var indexName = GetIndexName (tableName, columnNames); - var query = string.Format ("DROP INDEX IF EXISTS {0}", indexName); - - using (var command = connection.CreateCommand ()) { - command.CommandText = query; - command.ExecuteNonQuery (); - } - } - - void CreateCertificatesTable (DataTable table) - { - CreateTable (connection, table); - - var currentColumns = GetTableColumns (connection, table.TableName); - bool hasAnchorColumn = false; - - for (int i = 0; i < currentColumns.Count; i++) { - if (currentColumns[i].ColumnName.Equals ("ANCHOR", StringComparison.Ordinal)) { - hasAnchorColumn = true; - break; - } - } - - // Note: The ANCHOR, SUBJECTNAME and SUBJECTKEYIDENTIFIER columns were all added in the same version, - // so if the ANCHOR column is missing, they all are. - if (!hasAnchorColumn) { - using (var transaction = connection.BeginTransaction ()) { - try { - var column = table.Columns[table.Columns.IndexOf ("ANCHOR")]; - AddTableColumn (connection, table, column); - - column = table.Columns[table.Columns.IndexOf ("SUBJECTNAME")]; - AddTableColumn (connection, table, column); - - column = table.Columns[table.Columns.IndexOf ("SUBJECTKEYIDENTIFIER")]; - AddTableColumn (connection, table, column); - - foreach (var record in Find (null, false, X509CertificateRecordFields.Id | X509CertificateRecordFields.Certificate)) { - var statement = "UPDATE CERTIFICATES SET ANCHOR = @ANCHOR, SUBJECTNAME = @SUBJECTNAME, SUBJECTKEYIDENTIFIER = @SUBJECTKEYIDENTIFIER WHERE ID = @ID"; - var command = connection.CreateCommand (); - - command.AddParameterWithValue ("@ID", record.Id); - command.AddParameterWithValue ("@ANCHOR", record.IsAnchor); - command.AddParameterWithValue ("@SUBJECTNAME", record.SubjectName); - command.AddParameterWithValue ("@SUBJECTKEYIDENTIFIER", record.SubjectKeyIdentifier?.AsHex ()); - command.CommandType = CommandType.Text; - command.CommandText = statement; - - command.ExecuteNonQuery (); - } - - transaction.Commit (); - } catch { - transaction.Rollback (); - throw; - } - } - - // Remove some old indexes - RemoveIndex (connection, table.TableName, new[] { "TRUSTED" }); - RemoveIndex (connection, table.TableName, new[] { "TRUSTED", "BASICCONSTRAINTS", "ISSUERNAME", "SERIALNUMBER" }); - RemoveIndex (connection, table.TableName, new[] { "BASICCONSTRAINTS", "ISSUERNAME", "SERIALNUMBER" }); - RemoveIndex (connection, table.TableName, new[] { "BASICCONSTRAINTS", "FINGERPRINT" }); - RemoveIndex (connection, table.TableName, new[] { "BASICCONSTRAINTS", "SUBJECTEMAIL" }); - } - - // Note: Use "EXPLAIN QUERY PLAN SELECT ... FROM CERTIFICATES WHERE ..." to verify that any indexes we create get used as expected. - - // Index for matching against a specific certificate - CreateIndex (connection, table.TableName, new [] { "ISSUERNAME", "SERIALNUMBER", "FINGERPRINT" }); - - // Index for searching for a certificate based on a SecureMailboxAddress - CreateIndex (connection, table.TableName, new [] { "BASICCONSTRAINTS", "FINGERPRINT", "NOTBEFORE", "NOTAFTER" }); - - // Index for searching for a certificate based on a MailboxAddress - CreateIndex (connection, table.TableName, new [] { "BASICCONSTRAINTS", "SUBJECTEMAIL", "NOTBEFORE", "NOTAFTER" }); - - // Index for gathering a list of Trusted Anchors - CreateIndex (connection, table.TableName, new [] { "TRUSTED", "ANCHOR", "KEYUSAGE" }); - } - - void CreateCrlsTable (DataTable table) - { - CreateTable (connection, table); - - CreateIndex (connection, table.TableName, new [] { "ISSUERNAME" }); - CreateIndex (connection, table.TableName, new [] { "DELTA", "ISSUERNAME", "THISUPDATE" }); - } - - static StringBuilder CreateSelectQuery (X509CertificateRecordFields fields) - { - var query = new StringBuilder ("SELECT "); - var columns = GetColumnNames (fields); - - for (int i = 0; i < columns.Length; i++) { - if (i > 0) - query = query.Append (", "); - - query = query.Append (columns[i]); - } - - return query.Append (" FROM CERTIFICATES"); - } - - /// - /// Gets the database command to select the record matching the specified certificate. - /// - /// - /// Gets the database command to select the record matching the specified certificate. - /// - /// The database command. - /// The certificate. - /// The fields to return. - protected override DbCommand GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) - { - var fingerprint = certificate.GetFingerprint ().ToLowerInvariant (); - var serialNumber = certificate.SerialNumber.ToString (); - var issuerName = certificate.IssuerDN.ToString (); - var command = connection.CreateCommand (); - var query = CreateSelectQuery (fields); - - // FIXME: Is this really the best way to query for an exact match of a certificate? - query = query.Append (" WHERE ISSUERNAME = @ISSUERNAME AND SERIALNUMBER = @SERIALNUMBER AND FINGERPRINT = @FINGERPRINT LIMIT 1"); - command.AddParameterWithValue ("@ISSUERNAME", issuerName); - command.AddParameterWithValue ("@SERIALNUMBER", serialNumber); - command.AddParameterWithValue ("@FINGERPRINT", fingerprint); - - command.CommandText = query.ToString (); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Get the database command to select the certificate records for the specified mailbox. - /// - /// - /// Gets the database command to select the certificate records for the specified mailbox. - /// - /// The database command. - /// The mailbox. - /// The date and time for which the certificate should be valid. - /// true - /// The fields to return. - protected override DbCommand GetSelectCommand (MailboxAddress mailbox, DateTime now, bool requirePrivateKey, X509CertificateRecordFields fields) - { - var secure = mailbox as SecureMailboxAddress; - var command = connection.CreateCommand (); - var query = CreateSelectQuery (fields); - - query = query.Append (" WHERE BASICCONSTRAINTS = @BASICCONSTRAINTS "); - command.AddParameterWithValue ("@BASICCONSTRAINTS", -1); - - if (secure != null && !string.IsNullOrEmpty (secure.Fingerprint)) { - if (secure.Fingerprint.Length < 40) { - command.AddParameterWithValue ("@FINGERPRINT", secure.Fingerprint.ToLowerInvariant () + "%"); - query = query.Append ("AND FINGERPRINT LIKE @FINGERPRINT "); - } else { - command.AddParameterWithValue ("@FINGERPRINT", secure.Fingerprint.ToLowerInvariant ()); - query = query.Append ("AND FINGERPRINT = @FINGERPRINT "); - } - } else { - command.AddParameterWithValue ("@SUBJECTEMAIL", mailbox.Address.ToLowerInvariant ()); - query = query.Append ("AND SUBJECTEMAIL = @SUBJECTEMAIL "); - } - - query = query.Append ("AND NOTBEFORE < @NOW AND NOTAFTER > @NOW"); - command.AddParameterWithValue ("@NOW", now.ToUniversalTime ()); - - if (requirePrivateKey) - query = query.Append (" AND PRIVATEKEY IS NOT NULL"); - - command.CommandText = query.ToString (); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Get the database command to select the requested certificate records. - /// - /// - /// Gets the database command to select the requested certificate records. - /// - /// The database command. - /// The certificate selector. - /// true if only trusted anchor certificates should be matched; otherwise, false. - /// true if the certificate must have a private key; otherwise, false. - /// The fields to return. - protected override DbCommand GetSelectCommand (IX509Selector selector, bool trustedAnchorsOnly, bool requirePrivateKey, X509CertificateRecordFields fields) - { - var match = selector as X509CertStoreSelector; - var command = connection.CreateCommand (); - var query = CreateSelectQuery (fields); - int baseQueryLength = query.Length; - - query = query.Append (" WHERE "); - - // FIXME: We could create an X509CertificateDatabaseSelector subclass of X509CertStoreSelector that - // adds properties like bool Trusted, bool Anchor, and bool HasPrivateKey ? Then we could drop the - // bool method arguments... - if (trustedAnchorsOnly) { - query = query.Append ("TRUSTED = @TRUSTED AND ANCHOR = @ANCHOR"); - command.AddParameterWithValue ("@TRUSTED", true); - command.AddParameterWithValue ("@ANCHOR", true); - } - - if (match != null) { - if (match.BasicConstraints >= 0 || match.BasicConstraints == -2) { - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - if (match.BasicConstraints == -2) { - command.AddParameterWithValue ("@BASICCONSTRAINTS", -1); - query = query.Append ("BASICCONSTRAINTS = @BASICCONSTRAINTS"); - } else { - command.AddParameterWithValue ("@BASICCONSTRAINTS", match.BasicConstraints); - query = query.Append ("BASICCONSTRAINTS >= @BASICCONSTRAINTS"); - } - } - - if (match.CertificateValid != null) { - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - command.AddParameterWithValue ("@DATETIME", match.CertificateValid.Value.ToUniversalTime ()); - query = query.Append ("NOTBEFORE < @DATETIME AND NOTAFTER > @DATETIME"); - } - - if (match.Issuer != null || match.Certificate != null) { - // Note: GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) - // queries for ISSUERNAME, SERIALNUMBER, and FINGERPRINT so we'll do the same. - var issuer = match.Issuer ?? match.Certificate.IssuerDN; - - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - command.AddParameterWithValue ("@ISSUERNAME", issuer.ToString ()); - query = query.Append ("ISSUERNAME = @ISSUERNAME"); - } - - if (match.SerialNumber != null || match.Certificate != null) { - // Note: GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) - // queries for ISSUERNAME, SERIALNUMBER, and FINGERPRINT so we'll do the same. - var serialNumber = match.SerialNumber ?? match.Certificate.SerialNumber; - - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - command.AddParameterWithValue ("@SERIALNUMBER", serialNumber.ToString ()); - query = query.Append ("SERIALNUMBER = @SERIALNUMBER"); - } - - if (match.Certificate != null) { - // Note: GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields) - // queries for ISSUERNAME, SERIALNUMBER, and FINGERPRINT so we'll do the same. - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - command.AddParameterWithValue ("@FINGERPRINT", match.Certificate.GetFingerprint ()); - query = query.Append ("FINGERPRINT = @FINGERPRINT"); - } - - if (match.Subject != null) { - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - command.AddParameterWithValue ("@SUBJECTNAME", match.Subject.ToString ()); - query = query.Append ("SUBJECTNAME = @SUBJECTNAME"); - } - - if (match.SubjectKeyIdentifier != null) { - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - var id = (Asn1OctetString) Asn1Object.FromByteArray (match.SubjectKeyIdentifier); - var subjectKeyIdentifier = id.GetOctets ().AsHex (); - - command.AddParameterWithValue ("@SUBJECTKEYIDENTIFIER", subjectKeyIdentifier); - query = query.Append ("SUBJECTKEYIDENTIFIER = @SUBJECTKEYIDENTIFIER"); - } - - if (match.KeyUsage != null) { - var flags = BouncyCastleCertificateExtensions.GetKeyUsageFlags (match.KeyUsage); - - if (flags != X509KeyUsageFlags.None) { - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - command.AddParameterWithValue ("@FLAGS", (int) flags); - query = query.Append ("(KEYUSAGE = 0 OR (KEYUSAGE & @FLAGS) = @FLAGS)"); - } - } - } - - if (requirePrivateKey) { - if (command.Parameters.Count > 0) - query = query.Append (" AND "); - - query = query.Append ("PRIVATEKEY IS NOT NULL"); - } else if (command.Parameters.Count == 0) { - query.Length = baseQueryLength; - } - - command.CommandText = query.ToString (); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to select the CRL records matching the specified issuer. - /// - /// - /// Gets the database command to select the CRL records matching the specified issuer. - /// - /// The database command. - /// The issuer. - /// The fields to return. - protected override DbCommand GetSelectCommand (X509Name issuer, X509CrlRecordFields fields) - { - var query = "SELECT " + string.Join (", ", GetColumnNames (fields)) + " FROM CRLS "; - var command = connection.CreateCommand (); - - command.CommandText = query + "WHERE ISSUERNAME = @ISSUERNAME"; - command.AddParameterWithValue ("@ISSUERNAME", issuer.ToString ()); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to select the record for the specified CRL. - /// - /// - /// Gets the database command to select the record for the specified CRL. - /// - /// The database command. - /// The X.509 CRL. - /// The fields to return. - protected override DbCommand GetSelectCommand (X509Crl crl, X509CrlRecordFields fields) - { - var query = "SELECT " + string.Join (", ", GetColumnNames (fields)) + " FROM CRLS "; - var issuerName = crl.IssuerDN.ToString (); - var command = connection.CreateCommand (); - - command.CommandText = query + "WHERE DELTA = @DELTA AND ISSUERNAME = @ISSUERNAME AND THISUPDATE = @THISUPDATE LIMIT 1"; - command.AddParameterWithValue ("@DELTA", crl.IsDelta ()); - command.AddParameterWithValue ("@ISSUERNAME", issuerName); - command.AddParameterWithValue ("@THISUPDATE", crl.ThisUpdate.ToUniversalTime ()); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to select all CRLs in the table. - /// - /// - /// Gets the database command to select all CRLs in the table. - /// - /// The database command. - protected override DbCommand GetSelectAllCrlsCommand () - { - var command = connection.CreateCommand (); - - command.CommandText = "SELECT ID, CRL FROM CRLS"; - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to delete the specified certificate record. - /// - /// - /// Gets the database command to delete the specified certificate record. - /// - /// The database command. - /// The certificate record. - protected override DbCommand GetDeleteCommand (X509CertificateRecord record) - { - var command = connection.CreateCommand (); - - command.CommandText = "DELETE FROM CERTIFICATES WHERE ID = @ID"; - command.AddParameterWithValue ("@ID", record.Id); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to delete the specified CRL record. - /// - /// - /// Gets the database command to delete the specified CRL record. - /// - /// The database command. - /// The record. - protected override DbCommand GetDeleteCommand (X509CrlRecord record) - { - var command = connection.CreateCommand (); - - command.CommandText = "DELETE FROM CRLS WHERE ID = @ID"; - command.AddParameterWithValue ("@ID", record.Id); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to insert the specified certificate record. - /// - /// - /// Gets the database command to insert the specified certificate record. - /// - /// The database command. - /// The certificate record. - protected override DbCommand GetInsertCommand (X509CertificateRecord record) - { - var statement = new StringBuilder ("INSERT INTO CERTIFICATES("); - var variables = new StringBuilder ("VALUES("); - var command = connection.CreateCommand (); - var columns = certificatesTable.Columns; - - for (int i = 1; i < columns.Count; i++) { - if (i > 1) { - statement.Append (", "); - variables.Append (", "); - } - - var value = GetValue (record, columns[i].ColumnName); - var variable = "@" + columns[i]; - - command.AddParameterWithValue (variable, value); - statement.Append (columns[i]); - variables.Append (variable); - } - - statement.Append (')'); - variables.Append (')'); - - command.CommandText = statement + " " + variables; - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to insert the specified CRL record. - /// - /// - /// Gets the database command to insert the specified CRL record. - /// - /// The database command. - /// The CRL record. - protected override DbCommand GetInsertCommand (X509CrlRecord record) - { - var statement = new StringBuilder ("INSERT INTO CRLS("); - var variables = new StringBuilder ("VALUES("); - var command = connection.CreateCommand (); - var columns = crlsTable.Columns; - - for (int i = 1; i < columns.Count; i++) { - if (i > 1) { - statement.Append (", "); - variables.Append (", "); - } - - var value = GetValue (record, columns[i].ColumnName); - var variable = "@" + columns[i]; - - command.AddParameterWithValue (variable, value); - statement.Append (columns[i]); - variables.Append (variable); - } - - statement.Append (')'); - variables.Append (')'); - - command.CommandText = statement + " " + variables; - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to update the specified record. - /// - /// - /// Gets the database command to update the specified record. - /// - /// The database command. - /// The certificate record. - /// The fields to update. - protected override DbCommand GetUpdateCommand (X509CertificateRecord record, X509CertificateRecordFields fields) - { - var statement = new StringBuilder ("UPDATE CERTIFICATES SET "); - var columns = GetColumnNames (fields & ~X509CertificateRecordFields.Id); - var command = connection.CreateCommand (); - - for (int i = 0; i < columns.Length; i++) { - var value = GetValue (record, columns[i]); - var variable = "@" + columns[i]; - - if (i > 0) - statement.Append (", "); - - statement.Append (columns[i]); - statement.Append (" = "); - statement.Append (variable); - - command.AddParameterWithValue (variable, value); - } - - statement.Append (" WHERE ID = @ID"); - command.AddParameterWithValue ("@ID", record.Id); - - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Gets the database command to update the specified CRL record. - /// - /// - /// Gets the database command to update the specified CRL record. - /// - /// The database command. - /// The CRL record. - protected override DbCommand GetUpdateCommand (X509CrlRecord record) - { - var statement = new StringBuilder ("UPDATE CRLS SET "); - var command = connection.CreateCommand (); - var columns = crlsTable.Columns; - - for (int i = 1; i < columns.Count; i++) { - var value = GetValue (record, columns[i].ColumnName); - var variable = "@" + columns[i]; - - if (i > 1) - statement.Append (", "); - - statement.Append (columns[i]); - statement.Append (" = "); - statement.Append (variable); - - command.AddParameterWithValue (variable, value); - } - - statement.Append (" WHERE ID = @ID"); - command.AddParameterWithValue ("@ID", record.Id); - - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; - - return command; - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - if (connection != null) - connection.Dispose (); - disposed = true; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MimeKit/Cryptography/SqliteCertificateDatabase.cs b/src/MimeKit/Cryptography/SqliteCertificateDatabase.cs deleted file mode 100644 index c37e68f..0000000 --- a/src/MimeKit/Cryptography/SqliteCertificateDatabase.cs +++ /dev/null @@ -1,394 +0,0 @@ -// -// SqliteCertificateDatabase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Data; -using System.Text; -using System.Data.Common; -using System.Collections.Generic; - -#if __MOBILE__ -using Mono.Data.Sqlite; -#else -using System.Reflection; -#endif - -namespace MimeKit.Cryptography { - /// - /// An X.509 certificate database built on SQLite. - /// - /// - /// An X.509 certificate database is used for storing certificates, metdata related to the certificates - /// (such as encryption algorithms supported by the associated client), certificate revocation lists (CRLs), - /// and private keys. - /// This particular database uses SQLite to store the data. - /// - public class SqliteCertificateDatabase : SqlCertificateDatabase - { -#if !__MOBILE__ - class SQLiteAssembly - { - public Type ConnectionStringBuilderType { get; private set; } - public Type ConnectionType { get; private set; } - public Assembly Assembly { get; private set; } - - public PropertyInfo ConnectionStringProperty { get; private set; } - public PropertyInfo DateTimeFormatProperty { get; private set; } - public PropertyInfo DataSourceProperty { get; private set; } - - public static SQLiteAssembly Load (string assemblyName) - { - try { - int dot = assemblyName.LastIndexOf ('.'); - var prefix = assemblyName.Substring (dot + 1); - - var assembly = Assembly.Load (new AssemblyName (assemblyName)); - var builderType = assembly.GetType (assemblyName + "." + prefix + "ConnectionStringBuilder"); - var connectionType = assembly.GetType (assemblyName + "." + prefix + "Connection"); - var connectionString = builderType.GetProperty ("ConnectionString"); - var dateTimeFormat = builderType.GetProperty ("DateTimeFormat"); - var dataSource = builderType.GetProperty ("DataSource"); - - return new SQLiteAssembly { - Assembly = assembly, - ConnectionType = connectionType, - ConnectionStringBuilderType = builderType, - ConnectionStringProperty = connectionString, - DateTimeFormatProperty = dateTimeFormat, - DataSourceProperty = dataSource - }; - } catch { - return null; - } - } - } - - static readonly SQLiteAssembly sqliteAssembly; -#endif - - // At class initialization we try to use reflection to load the - // Mono.Data.Sqlite assembly: this allows us to use Sqlite as the - // default certificate store without explicitly depending on the - // assembly. - static SqliteCertificateDatabase () - { -#if __MOBILE__ - IsAvailable = true; -#else // !__MOBILE__ -#if NETFRAMEWORK || NETSTANDARD2_0 || NETCOREAPP3_0 - var platform = Environment.OSVersion.Platform; -#endif - -#if NETSTANDARD1_3 || NETSTANDARD1_6 || NETSTANDARD2_0 || NETCOREAPP3_0 - if ((sqliteAssembly = SQLiteAssembly.Load ("Microsoft.Data.Sqlite")) != null) { - // Make sure that the runtime can load the native sqlite library - if (VerifySQLiteAssemblyIsUsable ()) { - IsAvailable = true; - return; - } - } -#endif - -#if NETFRAMEWORK || NETCOREAPP3_0 - // Mono.Data.Sqlite will only work on Unix-based platforms. - if (platform == PlatformID.Unix || platform == PlatformID.MacOSX) { - if ((sqliteAssembly = SQLiteAssembly.Load ("Mono.Data.Sqlite")) != null) { - // Make sure that the runtime can load the native sqlite3 library - if (VerifySQLiteAssemblyIsUsable ()) { - IsAvailable = true; - return; - } - } - } -#endif - -#if NETFRAMEWORK || NETSTANDARD2_0 || NETCOREAPP3_0 - if ((sqliteAssembly = SQLiteAssembly.Load ("System.Data.SQLite")) != null) { - // Make sure that the runtime can load the native sqlite3 library - if (VerifySQLiteAssemblyIsUsable ()) { - IsAvailable = true; - return; - } - } -#endif -#endif // __MOBILE__ - } - -#if !__MOBILE__ - static bool VerifySQLiteAssemblyIsUsable () - { - // Make sure that the runtime can load the native sqlite3 library. - var fileName = Path.GetTempFileName (); - - try { - var connection = CreateConnection (fileName); - connection.Dispose (); - return true; - } catch { - return false; - } finally { - File.Delete (fileName); - } - } -#endif - - internal static bool IsAvailable { - get; private set; - } - - static DbConnection CreateConnection (string fileName) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (fileName.Length == 0) - throw new ArgumentException ("The file name cannot be empty.", nameof (fileName)); - - if (!File.Exists (fileName)) { - var dir = Path.GetDirectoryName (fileName); - - if (!string.IsNullOrEmpty (dir) && !Directory.Exists (dir)) - Directory.CreateDirectory (dir); - -#if __MOBILE__ - SqliteConnection.CreateFile (fileName); -#else - File.Create (fileName).Dispose (); -#endif - } - -#if !__MOBILE__ - var builder = Activator.CreateInstance (sqliteAssembly.ConnectionStringBuilderType); - - sqliteAssembly.DataSourceProperty.SetValue (builder, fileName, null); - sqliteAssembly.DateTimeFormatProperty?.SetValue (builder, 0, null); - - var connectionString = (string) sqliteAssembly.ConnectionStringProperty.GetValue (builder, null); - - return (DbConnection) Activator.CreateInstance (sqliteAssembly.ConnectionType, new [] { connectionString }); -#else - var builder = new SqliteConnectionStringBuilder (); - builder.DateTimeFormat = SQLiteDateFormats.Ticks; - builder.DataSource = fileName; - - return new SqliteConnection (builder.ConnectionString); -#endif - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new and opens a connection to the - /// SQLite database at the specified path using the Mono.Data.Sqlite binding to the native - /// SQLite library. - /// If Mono.Data.Sqlite is not available or if an alternative binding to the native - /// SQLite library is preferred, then consider using - /// instead. - /// - /// The file name. - /// The password used for encrypting and decrypting the private keys. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The specified file path is empty. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An error occurred reading the file. - /// - public SqliteCertificateDatabase (string fileName, string password) : this (CreateConnection (fileName), password) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new using the provided SQLite database connection. - /// - /// The SQLite connection. - /// The password used for encrypting and decrypting the private keys. - /// - /// is null. - /// -or- - /// is null. - /// - public SqliteCertificateDatabase (DbConnection connection, string password) : base (connection, password) - { - } - - /// - /// Gets the columns for the specified table. - /// - /// - /// Gets the list of columns for the specified table. - /// - /// The . - /// The name of the table. - /// The list of columns. - protected override IList GetTableColumns (DbConnection connection, string tableName) - { - using (var command = connection.CreateCommand ()) { - command.CommandText = $"PRAGMA table_info({tableName})"; - using (var reader = command.ExecuteReader ()) { - var columns = new List (); - - while (reader.Read ()) { - var column = new DataColumn (); - - for (int i = 0; i < reader.FieldCount; i++) { - var field = reader.GetName (i).ToUpperInvariant (); - - switch (field) { - case "NAME": - column.ColumnName = reader.GetString (i); - break; - case "TYPE": - var type = reader.GetString (i); - switch (type) { - case "INTEGER": column.DataType = typeof (long); break; - case "BLOB": column.DataType = typeof (byte[]); break; - case "TEXT": column.DataType = typeof (string); break; - } - break; - case "NOTNULL": - column.AllowDBNull = !reader.GetBoolean (i); - break; - } - } - - columns.Add (column); - } - - return columns; - } - } - } - - static void Build (StringBuilder statement, DataTable table, DataColumn column, ref int primaryKeys, bool create) - { - statement.Append (column.ColumnName); - statement.Append (' '); - - if (column.DataType == typeof (long) || column.DataType == typeof (int) || column.DataType == typeof (bool)) { - statement.Append ("INTEGER"); - } else if (column.DataType == typeof (byte[])) { - statement.Append ("BLOB"); - } else if (column.DataType == typeof (string)) { - statement.Append ("TEXT"); - } else { - throw new NotImplementedException (); - } - - bool isPrimaryKey = false; - if (table != null && table.PrimaryKey != null && primaryKeys < table.PrimaryKey.Length) { - for (int i = 0; i < table.PrimaryKey.Length; i++) { - if (column == table.PrimaryKey[i]) { - statement.Append (" PRIMARY KEY"); - isPrimaryKey = true; - primaryKeys++; - break; - } - } - } - - if (column.AutoIncrement) - statement.Append (" AUTOINCREMENT"); - - if (column.Unique && !isPrimaryKey) - statement.Append (" UNIQUE"); - - // Note: Normally we'd want to include NOT NULL, but we can't *add* new columns with the NOT NULL restriction - if (create && !column.AllowDBNull) - statement.Append (" NOT NULL"); - } - - /// - /// Create a table. - /// - /// - /// Creates the specified table. - /// - /// The . - /// The table. - protected override void CreateTable (DbConnection connection, DataTable table) - { - var statement = new StringBuilder ("CREATE TABLE IF NOT EXISTS "); - int primaryKeys = 0; - - statement.Append (table.TableName); - statement.Append ('('); - - foreach (DataColumn column in table.Columns) { - Build (statement, table, column, ref primaryKeys, true); - statement.Append (", "); - } - - if (table.Columns.Count > 0) - statement.Length -= 2; - - statement.Append (')'); - - using (var command = connection.CreateCommand ()) { - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; - command.ExecuteNonQuery (); - } - } - - /// - /// Adds a column to a table. - /// - /// - /// Adds a column to a table. - /// - /// The . - /// The table. - /// The column to add. - protected override void AddTableColumn (DbConnection connection, DataTable table, DataColumn column) - { - var statement = new StringBuilder ("ALTER TABLE "); - int primaryKeys = table.PrimaryKey?.Length ?? 0; - - statement.Append (table.TableName); - statement.Append (" ADD COLUMN "); - Build (statement, table, column, ref primaryKeys, false); - - using (var command = connection.CreateCommand ()) { - command.CommandText = statement.ToString (); - command.CommandType = CommandType.Text; - command.ExecuteNonQuery (); - } - } - } -} diff --git a/src/MimeKit/Cryptography/SubjectIdentifierType.cs b/src/MimeKit/Cryptography/SubjectIdentifierType.cs deleted file mode 100644 index 6206f45..0000000 --- a/src/MimeKit/Cryptography/SubjectIdentifierType.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// SubjectIdentifierType.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Cryptography { - /// - /// The method to use for identifying a certificate. - /// - /// - /// The method to use for identifying a certificate. - /// - public enum SubjectIdentifierType { - /// - /// The identifier type is unknown. - /// - Unknown, - - /// - /// Identify the certificate by its Issuer and Serial Number properties. - /// - IssuerAndSerialNumber, - - /// - /// Identify the certificate by the sha1 hash of its public key. - /// - SubjectKeyIdentifier, - } -} diff --git a/src/MimeKit/Cryptography/TemporarySecureMimeContext.cs b/src/MimeKit/Cryptography/TemporarySecureMimeContext.cs deleted file mode 100644 index 7400847..0000000 --- a/src/MimeKit/Cryptography/TemporarySecureMimeContext.cs +++ /dev/null @@ -1,462 +0,0 @@ -// -// TemporarySecureMimeContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections.Generic; - -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.Pkix; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.X509.Store; -using Org.BouncyCastle.Asn1.X509; - -namespace MimeKit.Cryptography { - /// - /// An S/MIME context that does not persist certificates, private keys or CRLs. - /// - /// - /// A is a special S/MIME context that - /// does not use a persistent store for certificates, private keys, or CRLs. - /// Instead, certificates, private keys, and CRLs are maintained in memory only. - /// - public class TemporarySecureMimeContext : BouncyCastleSecureMimeContext - { - readonly Dictionary capabilities; - internal readonly Dictionary keys; - internal readonly List certificates; - readonly HashSet fingerprints; - readonly List crls; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public TemporarySecureMimeContext () - { - capabilities = new Dictionary (StringComparer.Ordinal); - keys = new Dictionary (StringComparer.Ordinal); - certificates = new List (); - fingerprints = new HashSet (); - crls = new List (); - } - - /// - /// Check whether or not a particular mailbox address can be used for signing. - /// - /// - /// Checks whether or not as particular mailbocx address can be used for signing. - /// - /// true if the mailbox address can be used for signing; otherwise, false. - /// The signer. - /// - /// is null. - /// - public override bool CanSign (MailboxAddress signer) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - AsymmetricKeyParameter key; - - return GetCmsSignerCertificate (signer, out key) != null; - } - - /// - /// Check whether or not the cryptography context can encrypt to a particular recipient. - /// - /// - /// Checks whether or not the cryptography context can be used to encrypt to a particular recipient. - /// - /// true if the cryptography context can be used to encrypt to the designated recipient; otherwise, false. - /// The recipient's mailbox address. - /// - /// is null. - /// - public override bool CanEncrypt (MailboxAddress mailbox) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - return GetCmsRecipientCertificate (mailbox) != null; - } - - #region implemented abstract members of SecureMimeContext - - /// - /// Gets the X.509 certificate matching the specified selector. - /// - /// - /// Gets the first certificate that matches the specified selector. - /// - /// The certificate on success; otherwise null. - /// The search criteria for the certificate. - protected override X509Certificate GetCertificate (IX509Selector selector) - { - if (selector == null && certificates.Count > 0) - return certificates[0]; - - foreach (var certificate in certificates) { - if (selector.Match (certificate)) - return certificate; - } - - return null; - } - - /// - /// Gets the private key for the certificate matching the specified selector. - /// - /// - /// Gets the private key for the first certificate that matches the specified selector. - /// - /// The private key on success; otherwise null. - /// The search criteria for the private key. - protected override AsymmetricKeyParameter GetPrivateKey (IX509Selector selector) - { - foreach (var certificate in certificates) { - var fingerprint = certificate.GetFingerprint (); - AsymmetricKeyParameter key; - - if (!keys.TryGetValue (fingerprint, out key)) - continue; - - if (selector != null && !selector.Match (certificate)) - continue; - - return key; - } - - return null; - } - - /// - /// Gets the trusted anchors. - /// - /// - /// A trusted anchor is a trusted root-level X.509 certificate, - /// generally issued by a certificate authority (CA). - /// - /// The trusted anchors. - protected override Org.BouncyCastle.Utilities.Collections.HashSet GetTrustedAnchors () - { - var anchors = new Org.BouncyCastle.Utilities.Collections.HashSet (); - - foreach (var certificate in certificates) { - var keyUsage = certificate.GetKeyUsage (); - - if (keyUsage != null && keyUsage[(int) X509KeyUsageBits.KeyCertSign] && certificate.IsSelfSigned ()) - anchors.Add (new TrustAnchor (certificate, null)); - } - - return anchors; - } - - /// - /// Gets the intermediate certificates. - /// - /// - /// An intermediate certificate is any certificate that exists between the root - /// certificate issued by a Certificate Authority (CA) and the certificate at - /// the end of the chain. - /// - /// The intermediate certificates. - protected override IX509Store GetIntermediateCertificates () - { - var intermediates = new X509CertificateStore (); - - foreach (var certificate in certificates) { - var keyUsage = certificate.GetKeyUsage (); - - if (keyUsage != null && keyUsage[(int) X509KeyUsageBits.KeyCertSign] && !certificate.IsSelfSigned ()) - intermediates.Add (certificate); - } - - return intermediates; - } - - /// - /// Gets the certificate revocation lists. - /// - /// - /// A Certificate Revocation List (CRL) is a list of certificate serial numbers issued - /// by a particular Certificate Authority (CA) that have been revoked, either by the CA - /// itself or by the owner of the revoked certificate. - /// - /// The certificate revocation lists. - protected override IX509Store GetCertificateRevocationLists () - { - return X509StoreFactory.Create ("Crl/Collection", new X509CollectionStoreParameters (crls)); - } - - /// - /// Get the date & time for the next scheduled certificate revocation list update for the specified issuer. - /// - /// - /// Gets the date & time for the next scheduled certificate revocation list update for the specified issuer. - /// - /// The date & time for the next update. - /// The issuer. - protected override DateTime GetNextCertificateRevocationListUpdate (X509Name issuer) - { - var nextUpdate = DateTime.MinValue.ToUniversalTime (); - - foreach (var crl in crls) { - if (!crl.IssuerDN.Equals (issuer)) - continue; - - nextUpdate = crl.NextUpdate.Value > nextUpdate ? crl.NextUpdate.Value : nextUpdate; - } - - return nextUpdate; - } - - X509Certificate GetCmsRecipientCertificate (MailboxAddress mailbox) - { - var secure = mailbox as SecureMailboxAddress; - var now = DateTime.UtcNow; - - foreach (var certificate in certificates) { - if (certificate.NotBefore > now || certificate.NotAfter < now) - continue; - - var keyUsage = certificate.GetKeyUsageFlags (); - if (keyUsage != 0 && (keyUsage & X509KeyUsageFlags.KeyEncipherment) == 0) - continue; - - if (secure != null) { - var fingerprint = certificate.GetFingerprint (); - - if (!fingerprint.Equals (secure.Fingerprint, StringComparison.OrdinalIgnoreCase)) - continue; - } else { - var address = certificate.GetSubjectEmailAddress (); - - if (!address.Equals (mailbox.Address, StringComparison.OrdinalIgnoreCase)) - continue; - } - - return certificate; - } - - return null; - } - - /// - /// Gets the for the specified mailbox. - /// - /// - /// Constructs a with the appropriate certificate and - /// for the specified mailbox. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address. - /// - /// A . - /// The mailbox. - /// - /// A certificate for the specified could not be found. - /// - protected override CmsRecipient GetCmsRecipient (MailboxAddress mailbox) - { - X509Certificate certificate; - - if ((certificate = GetCmsRecipientCertificate (mailbox)) == null) - throw new CertificateNotFoundException (mailbox, "A valid certificate could not be found."); - - var recipient = new CmsRecipient (certificate); - EncryptionAlgorithm[] algorithms; - - if (capabilities.TryGetValue (certificate.GetFingerprint (), out algorithms)) - recipient.EncryptionAlgorithms = algorithms; - - return recipient; - } - - X509Certificate GetCmsSignerCertificate (MailboxAddress mailbox, out AsymmetricKeyParameter key) - { - var secure = mailbox as SecureMailboxAddress; - var now = DateTime.UtcNow; - - foreach (var certificate in certificates) { - if (certificate.NotBefore > now || certificate.NotAfter < now) - continue; - - var keyUsage = certificate.GetKeyUsageFlags (); - if (keyUsage != 0 && (keyUsage & DigitalSignatureKeyUsageFlags) == 0) - continue; - - var fingerprint = certificate.GetFingerprint (); - - if (!keys.TryGetValue (fingerprint, out key)) - continue; - - if (secure != null) { - if (!fingerprint.Equals (secure.Fingerprint, StringComparison.OrdinalIgnoreCase)) - continue; - } else { - var address = certificate.GetSubjectEmailAddress (); - - if (!address.Equals (mailbox.Address, StringComparison.OrdinalIgnoreCase)) - continue; - } - - return certificate; - } - - key = null; - - return null; - } - - /// - /// Gets the for the specified mailbox. - /// - /// - /// Constructs a with the appropriate signing certificate - /// for the specified mailbox. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address for database lookups. - /// - /// A . - /// The mailbox. - /// The preferred digest algorithm. - /// - /// A certificate for the specified could not be found. - /// - protected override CmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgorithm digestAlgo) - { - X509Certificate certificate; - AsymmetricKeyParameter key; - - if ((certificate = GetCmsSignerCertificate (mailbox, out key)) == null) - throw new CertificateNotFoundException (mailbox, "A valid signing certificate could not be found."); - - return new CmsSigner (BuildCertificateChain (certificate), key) { - DigestAlgorithm = digestAlgo - }; - } - - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// - /// The certificate. - /// The encryption algorithm capabilities of the client (in preferred order). - /// The timestamp. - protected override void UpdateSecureMimeCapabilities (X509Certificate certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp) - { - capabilities[certificate.GetFingerprint ()] = algorithms; - } - - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// The raw certificate and key data in pkcs12 format. - /// The password to unlock the stream. - /// - /// is null. - /// -or- - /// is null. - /// - public override void Import (Stream stream, string password) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ()); - - foreach (string alias in pkcs12.Aliases) { - if (pkcs12.IsKeyEntry (alias)) { - var chain = pkcs12.GetCertificateChain (alias); - var entry = pkcs12.GetKey (alias); - - for (int i = 0; i < chain.Length; i++) - Import (chain[i].Certificate); - - var fingerprint = chain[0].Certificate.GetFingerprint (); - if (!keys.ContainsKey (fingerprint)) - keys.Add (fingerprint, entry.Key); - } else if (pkcs12.IsCertificateEntry (alias)) { - var entry = pkcs12.GetCertificate (alias); - - Import (entry.Certificate); - } - } - } - - /// - /// Imports the specified certificate. - /// - /// - /// Imports the specified certificate. - /// - /// The certificate. - /// - /// is null. - /// - public override void Import (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - if (fingerprints.Add (certificate.GetFingerprint ())) - certificates.Add (certificate); - } - - /// - /// Imports the specified certificate revocation list. - /// - /// - /// Imports the specified certificate revocation list. - /// - /// The certificate revocation list. - /// - /// is null. - /// - public override void Import (X509Crl crl) - { - if (crl == null) - throw new ArgumentNullException (nameof (crl)); - - crls.Add (crl); - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/WindowsSecureMimeContext.cs b/src/MimeKit/Cryptography/WindowsSecureMimeContext.cs deleted file mode 100644 index 6d78974..0000000 --- a/src/MimeKit/Cryptography/WindowsSecureMimeContext.cs +++ /dev/null @@ -1,1248 +0,0 @@ -// -// WindowsSecureMimeContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Pkcs; -using System.Security.Cryptography.X509Certificates; - -using Org.BouncyCastle.Cms; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Asn1.Smime; - -using RealCmsSigner = System.Security.Cryptography.Pkcs.CmsSigner; -using RealCmsRecipient = System.Security.Cryptography.Pkcs.CmsRecipient; -using RealAlgorithmIdentifier = System.Security.Cryptography.Pkcs.AlgorithmIdentifier; -using RealSubjectIdentifierType = System.Security.Cryptography.Pkcs.SubjectIdentifierType; -using RealCmsRecipientCollection = System.Security.Cryptography.Pkcs.CmsRecipientCollection; -using RealX509KeyUsageFlags = System.Security.Cryptography.X509Certificates.X509KeyUsageFlags; - -using MimeKit.IO; - -namespace MimeKit.Cryptography { - /// - /// A Secure MIME (S/MIME) cryptography context. - /// - /// - /// An S/MIME cryptography context that uses - /// for certificate storage and retrieval. - /// - public class WindowsSecureMimeContext : SecureMimeContext - { - const X509KeyStorageFlags DefaultKeyStorageFlags = X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The X.509 store location. - public WindowsSecureMimeContext (StoreLocation location) - { - StoreLocation = location; - - // System.Security does not support Camellia... - Disable (EncryptionAlgorithm.Camellia256); - Disable (EncryptionAlgorithm.Camellia192); - Disable (EncryptionAlgorithm.Camellia192); - - // ... or Blowfish/Twofish... - Disable (EncryptionAlgorithm.Blowfish); - Disable (EncryptionAlgorithm.Twofish); - - // ...or CAST5... - Disable (EncryptionAlgorithm.Cast5); - - // ...or IDEA... - Disable (EncryptionAlgorithm.Idea); - - // ...or SEED... - Disable (EncryptionAlgorithm.Seed); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Constructs an S/MIME context using the current user's X.509 store location. - /// - public WindowsSecureMimeContext () : this (StoreLocation.CurrentUser) - { - } - - /// - /// Gets the X.509 store location. - /// - /// - /// Gets the X.509 store location. - /// - /// The store location. - public StoreLocation StoreLocation { - get; private set; - } - - /// - /// Check whether or not a particular mailbox address can be used for signing. - /// - /// - /// Checks whether or not as particular mailbocx address can be used for signing. - /// - /// true if the mailbox address can be used for signing; otherwise, false. - /// The signer. - /// - /// is null. - /// - public override bool CanSign (MailboxAddress signer) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - return GetSignerCertificate (signer) != null; - } - - /// - /// Check whether or not the cryptography context can encrypt to a particular recipient. - /// - /// - /// Checks whether or not the cryptography context can be used to encrypt to a particular recipient. - /// - /// true if the cryptography context can be used to encrypt to the designated recipient; otherwise, false. - /// The recipient's mailbox address. - /// - /// is null. - /// - public override bool CanEncrypt (MailboxAddress mailbox) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - return GetRecipientCertificate (mailbox) != null; - } - - #region implemented abstract members of SecureMimeContext - - /// - /// Get the certificate for the specified recipient. - /// - /// - /// Gets the certificate for the specified recipient. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address. - /// - /// The certificate to use for the recipient; otherwise, or null. - /// The recipient's mailbox address. - protected virtual X509Certificate2 GetRecipientCertificate (MailboxAddress mailbox) - { - var storeNames = new [] { StoreName.AddressBook, StoreName.My, StoreName.TrustedPeople }; - var secure = mailbox as SecureMailboxAddress; - var now = DateTime.UtcNow; - - foreach (var storeName in storeNames) { - var store = new X509Store (storeName, StoreLocation); - - store.Open (OpenFlags.ReadOnly); - - try { - foreach (var certificate in store.Certificates) { - if (certificate.NotBefore > now || certificate.NotAfter < now) - continue; - - var usage = certificate.Extensions[X509Extensions.KeyUsage.Id] as X509KeyUsageExtension; - if (usage != null && (usage.KeyUsages & RealX509KeyUsageFlags.KeyEncipherment) == 0) - continue; - - if (secure != null) { - if (!certificate.Thumbprint.Equals (secure.Fingerprint, StringComparison.OrdinalIgnoreCase)) - continue; - } else { - var address = certificate.GetNameInfo (X509NameType.EmailName, false); - - if (!address.Equals (mailbox.Address, StringComparison.InvariantCultureIgnoreCase)) - continue; - } - - return certificate; - } - } finally { - store.Close (); - } - } - - return null; - } - - /// - /// Get the for the specified mailbox. - /// - /// - /// Constructs a with - /// the appropriate certificate for the specified mailbox. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address. - /// - /// A . - /// The recipient's mailbox address. - /// - /// A certificate for the specified could not be found. - /// - protected virtual RealCmsRecipient GetCmsRecipient (MailboxAddress mailbox) - { - X509Certificate2 certificate; - - if ((certificate = GetRecipientCertificate (mailbox)) == null) - throw new CertificateNotFoundException (mailbox, "A valid certificate could not be found."); - - return new RealCmsRecipient (certificate); - } - - /// - /// Get a collection of for the specified mailboxes. - /// - /// - /// Gets a collection of for the specified mailboxes. - /// - /// A . - /// The recipient mailboxes. - /// - /// is null. - /// - /// - /// A certificate for one or more of the specified could not be found. - /// - RealCmsRecipientCollection GetCmsRecipients (IEnumerable mailboxes) - { - var collection = new RealCmsRecipientCollection (); - - foreach (var recipient in mailboxes) - collection.Add (GetCmsRecipient (recipient)); - - if (collection.Count == 0) - throw new ArgumentException ("No recipients specified.", nameof (mailboxes)); - - return collection; - } - - RealCmsRecipientCollection GetCmsRecipients (CmsRecipientCollection recipients) - { - var collection = new RealCmsRecipientCollection (); - - foreach (var recipient in recipients) { - var certificate = new X509Certificate2 (recipient.Certificate.GetEncoded ()); - RealSubjectIdentifierType type; - RealCmsRecipient real; - - if (recipient.RecipientIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - type = RealSubjectIdentifierType.IssuerAndSerialNumber; - else - type = RealSubjectIdentifierType.SubjectKeyIdentifier; - -#if NETCOREAPP3_0 - var padding = recipient.RsaEncryptionPadding?.AsRSAEncryptionPadding (); - - if (padding != null) - real = new RealCmsRecipient (type, certificate, padding); - else - real = new RealCmsRecipient (type, certificate); -#else - if (recipient.RsaEncryptionPadding?.Scheme == RsaEncryptionPaddingScheme.Oaep) - throw new NotSupportedException ("The RSAES-OAEP encryption padding scheme is not supported by the WindowsSecureMimeContext. You must use a subclass of BouncyCastleSecureMimeContext to get this feature."); - - real = new RealCmsRecipient (type, certificate); -#endif - - collection.Add (real); - } - - return collection; - } - - /// - /// Get the certificate for the specified signer. - /// - /// - /// Gets the certificate for the specified signer. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address. - /// - /// The certificate to use for the signer; otherwise, or null. - /// The signer's mailbox address. - protected virtual X509Certificate2 GetSignerCertificate (MailboxAddress mailbox) - { - var store = new X509Store (StoreName.My, StoreLocation); - var secure = mailbox as SecureMailboxAddress; - var now = DateTime.UtcNow; - - store.Open (OpenFlags.ReadOnly); - - try { - foreach (var certificate in store.Certificates) { - if (certificate.NotBefore > now || certificate.NotAfter < now) - continue; - - var usage = certificate.Extensions[X509Extensions.KeyUsage.Id] as X509KeyUsageExtension; - if (usage != null && (usage.KeyUsages & (RealX509KeyUsageFlags.DigitalSignature | RealX509KeyUsageFlags.NonRepudiation)) == 0) - continue; - - if (!certificate.HasPrivateKey) - continue; - - if (secure != null) { - if (!certificate.Thumbprint.Equals (secure.Fingerprint, StringComparison.OrdinalIgnoreCase)) - continue; - } else { - var address = certificate.GetNameInfo (X509NameType.EmailName, false); - - if (!address.Equals (mailbox.Address, StringComparison.InvariantCultureIgnoreCase)) - continue; - } - - return certificate; - } - } finally { - store.Close (); - } - - return null; - } - - AsnEncodedData GetSecureMimeCapabilities () - { - var attr = GetSecureMimeCapabilitiesAttribute (false); - - return new AsnEncodedData (attr.AttrType.Id, attr.AttrValues[0].GetEncoded ()); - } - - RealCmsSigner GetRealCmsSigner (RealSubjectIdentifierType type, X509Certificate2 certificate, DigestAlgorithm digestAlgo) - { - var signer = new RealCmsSigner (type, certificate); - signer.DigestAlgorithm = new Oid (GetDigestOid (digestAlgo)); - signer.SignedAttributes.Add (GetSecureMimeCapabilities ()); - signer.SignedAttributes.Add (new Pkcs9SigningTime ()); - signer.IncludeOption = X509IncludeOption.ExcludeRoot; - return signer; - } - - RealCmsSigner GetRealCmsSigner (CmsSigner signer) - { - if (signer.RsaSignaturePadding == RsaSignaturePadding.Pss) - throw new NotSupportedException ("The RSASSA-PSS signature padding scheme is not supported by the WindowsSecureMimeContext. You must use a subclass of BouncyCastleSecureMimeContext to get this feature."); - - var certificate = signer.Certificate.AsX509Certificate2 (); - RealSubjectIdentifierType type; - - if (signer.SignerIdentifierType != SubjectIdentifierType.SubjectKeyIdentifier) - type = RealSubjectIdentifierType.IssuerAndSerialNumber; - else - type = RealSubjectIdentifierType.SubjectKeyIdentifier; - - certificate.PrivateKey = signer.PrivateKey.AsAsymmetricAlgorithm (); - - return GetRealCmsSigner (type, certificate, signer.DigestAlgorithm); - } - - /// - /// Get the for the specified mailbox. - /// - /// - /// Constructs a with - /// the appropriate signing certificate for the specified mailbox. - /// If the mailbox is a , the - /// property will be used instead of - /// the mailbox address for database lookups. - /// - /// A . - /// The signer's mailbox address. - /// The preferred digest algorithm. - /// - /// A certificate for the specified could not be found. - /// - protected virtual RealCmsSigner GetCmsSigner (MailboxAddress mailbox, DigestAlgorithm digestAlgo) - { - X509Certificate2 certificate; - - if ((certificate = GetSignerCertificate (mailbox)) == null) - throw new CertificateNotFoundException (mailbox, "A valid signing certificate could not be found."); - - return GetRealCmsSigner (RealSubjectIdentifierType.IssuerAndSerialNumber, certificate, digestAlgo); - } - - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// - /// - /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. - /// This method is called when decoding digital signatures that include S/MIME capabilities in the metadata, allowing custom - /// implementations to update the X.509 certificate records with the list of preferred encryption algorithms specified by the - /// sending client. - /// - /// The certificate. - /// The encryption algorithm capabilities of the client (in preferred order). - /// The timestamp. - protected virtual void UpdateSecureMimeCapabilities (X509Certificate2 certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp) - { - // TODO: implement this - should we add/update the X509Extension for S/MIME Capabilities? - } - - static byte[] ReadAllBytes (Stream stream) - { - if (stream is MemoryBlockStream) - return ((MemoryBlockStream) stream).ToArray (); - - if (stream is MemoryStream) - return ((MemoryStream) stream).ToArray (); - - using (var memory = new MemoryBlockStream ()) { - stream.CopyTo (memory, 4096); - return memory.ToArray (); - } - } - - static async Task ReadAllBytesAsync (Stream stream, CancellationToken cancellationToken) - { - if (stream is MemoryBlockStream) - return ((MemoryBlockStream) stream).ToArray (); - - if (stream is MemoryStream) - return ((MemoryStream) stream).ToArray (); - - using (var memory = new MemoryBlockStream ()) { - await stream.CopyToAsync (memory, 4096, cancellationToken).ConfigureAwait (false); - return memory.ToArray (); - } - } - - Stream Sign (RealCmsSigner signer, Stream content, bool detach) - { - var contentInfo = new ContentInfo (ReadAllBytes (content)); - var signed = new SignedCms (contentInfo, detach); - - try { - signed.ComputeSignature (signer, false); - } catch (CryptographicException) { - signer.IncludeOption = X509IncludeOption.EndCertOnly; - signed.ComputeSignature (signer, false); - } - - var signedData = signed.Encode (); - - return new MemoryStream (signedData, false); - } - - /// - /// Cryptographically signs and encapsulates the content using the specified signer. - /// - /// - /// Cryptographically signs and encapsulates the content using the specified signer. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override ApplicationPkcs7Mime EncapsulatedSign (CmsSigner signer, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var real = GetRealCmsSigner (signer); - - return new ApplicationPkcs7Mime (SecureMimeType.SignedData, Sign (real, content, false)); - } - - /// - /// Sign and encapsulate the content using the specified signer. - /// - /// - /// Sign and encapsulate the content using the specified signer. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - /// - /// A signing certificate could not be found for . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override ApplicationPkcs7Mime EncapsulatedSign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var real = GetCmsSigner (signer, digestAlgo); - - return new ApplicationPkcs7Mime (SecureMimeType.SignedData, Sign (real, content, false)); - } - - /// - /// Cryptographically signs the content using the specified signer. - /// - /// - /// Cryptographically signs the content using the specified signer. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override ApplicationPkcs7Signature Sign (CmsSigner signer, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var real = GetRealCmsSigner (signer); - - return new ApplicationPkcs7Signature (Sign (real, content, true)); - } - - /// - /// Sign the content using the specified signer. - /// - /// - /// Sign the content using the specified signer. - /// - /// A new instance - /// containing the detached signature data. - /// The signer. - /// The digest algorithm to use for signing. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// The specified is not supported by this context. - /// - /// - /// A signing certificate could not be found for . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override MimePart Sign (MailboxAddress signer, DigestAlgorithm digestAlgo, Stream content) - { - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var cmsSigner = GetCmsSigner (signer, digestAlgo); - - return new ApplicationPkcs7Signature (Sign (cmsSigner, content, true)); - } - - /// - /// Attempts to map a - /// to a . - /// - /// - /// Attempts to map a - /// to a . - /// - /// true if the algorithm identifier was successfully mapped; otherwise, false. - /// The algorithm identifier. - /// The encryption algorithm. - /// - /// is null. - /// - internal protected static bool TryGetDigestAlgorithm (Oid identifier, out DigestAlgorithm algorithm) - { - if (identifier == null) - throw new ArgumentNullException (nameof (identifier)); - - return TryGetDigestAlgorithm (identifier.Value, out algorithm); - } - - DigitalSignatureCollection GetDigitalSignatures (SignedCms signed) - { - var signatures = new List (); - - foreach (var signerInfo in signed.SignerInfos) { - var signature = new WindowsSecureMimeDigitalSignature (signerInfo); - - if (signature.EncryptionAlgorithms.Length > 0 && signature.CreationDate.Ticks != 0) { - UpdateSecureMimeCapabilities (signerInfo.Certificate, signature.EncryptionAlgorithms, signature.CreationDate); - } else { - try { - Import (signerInfo.Certificate); - } catch { - } - } - - signatures.Add (signature); - } - - return new DigitalSignatureCollection (signatures); - } - - /// - /// Verify the specified content using the detached signature data. - /// - /// - /// Verifies the specified content using the detached signature data. - /// - /// A list of the digital signatures. - /// The content. - /// The detached signature data. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override DigitalSignatureCollection Verify (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) - { - if (content == null) - throw new ArgumentNullException (nameof (content)); - - if (signatureData == null) - throw new ArgumentNullException (nameof (signatureData)); - - var contentInfo = new ContentInfo (ReadAllBytes (content)); - var signed = new SignedCms (contentInfo, true); - - signed.Decode (ReadAllBytes (signatureData)); - - return GetDigitalSignatures (signed); - } - - /// - /// Asynchronously verify the specified content using the detached signature data. - /// - /// - /// Verifies the specified content using the detached signature data. - /// - /// A list of the digital signatures. - /// The content. - /// The detached signature data. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override async Task VerifyAsync (Stream content, Stream signatureData, CancellationToken cancellationToken = default (CancellationToken)) - { - if (content == null) - throw new ArgumentNullException (nameof (content)); - - if (signatureData == null) - throw new ArgumentNullException (nameof (signatureData)); - - var contentInfo = new ContentInfo (await ReadAllBytesAsync (content, cancellationToken).ConfigureAwait (false)); - var signed = new SignedCms (contentInfo, true); - - signed.Decode (await ReadAllBytesAsync (signatureData, cancellationToken).ConfigureAwait (false)); - - return GetDigitalSignatures (signed); - } - - /// - /// Verify the digital signatures of the specified signed data and extract the original content. - /// - /// - /// Verifies the digital signatures of the specified signed data and extracts the original content. - /// - /// The list of digital signatures. - /// The signed data. - /// The extracted MIME entity. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The extracted content could not be parsed as a MIME entity. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override DigitalSignatureCollection Verify (Stream signedData, out MimeEntity entity, CancellationToken cancellationToken = default (CancellationToken)) - { - if (signedData == null) - throw new ArgumentNullException (nameof (signedData)); - - var content = ReadAllBytes (signedData); - var signed = new SignedCms (); - - signed.Decode (content); - - var memory = new MemoryStream (signed.ContentInfo.Content, false); - - try { - entity = MimeEntity.Load (memory, true, cancellationToken); - } catch { - memory.Dispose (); - throw; - } - - return GetDigitalSignatures (signed); - } - - /// - /// Verify the digital signatures of the specified signed data and extract the original content. - /// - /// - /// Verifies the digital signatures of the specified signed data and extracts the original content. - /// - /// The extracted content stream. - /// The signed data. - /// The digital signatures. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override Stream Verify (Stream signedData, out DigitalSignatureCollection signatures, CancellationToken cancellationToken = default (CancellationToken)) - { - if (signedData == null) - throw new ArgumentNullException (nameof (signedData)); - - var content = ReadAllBytes (signedData); - var signed = new SignedCms (); - - signed.Decode (content); - - signatures = GetDigitalSignatures (signed); - - return new MemoryStream (signed.ContentInfo.Content, false); - } - - /// - /// Gets the preferred encryption algorithm to use for encrypting to the specified recipients. - /// - /// - /// Gets the preferred encryption algorithm to use for encrypting to the specified recipients - /// based on the encryption algorithms supported by each of the recipients, the - /// , and the - /// . - /// If the supported encryption algorithms are unknown for any recipient, it is assumed that - /// the recipient supports at least the Triple-DES encryption algorithm. - /// - /// The preferred encryption algorithm. - /// The recipients. - protected virtual EncryptionAlgorithm GetPreferredEncryptionAlgorithm (RealCmsRecipientCollection recipients) - { - var votes = new int[EncryptionAlgorithmCount]; - int need = recipients.Count; - - foreach (var recipient in recipients) { - var supported = recipient.Certificate.GetEncryptionAlgorithms (); - - foreach (var algorithm in supported) - votes[(int) algorithm]++; - } - - // Starting with S/MIME v3 (published in 1999), Triple-DES is a REQUIRED algorithm. - // S/MIME v2.x and older only required RC2/40, but SUGGESTED Triple-DES. - // Considering the fact that Bruce Schneier was able to write a - // screensaver that could crack RC2/40 back in the late 90's, let's - // not default to anything weaker than Triple-DES... - EncryptionAlgorithm chosen = EncryptionAlgorithm.TripleDes; - int nvotes = 0; - - votes[(int) EncryptionAlgorithm.TripleDes] = need; - - // iterate through the algorithms, from strongest to weakest, keeping track - // of the algorithm with the most amount of votes (between algorithms with - // the same number of votes, choose the strongest of the 2 - i.e. the one - // that we arrive at first). - var algorithms = EncryptionAlgorithmRank; - for (int i = 0; i < algorithms.Length; i++) { - var algorithm = algorithms[i]; - - if (!IsEnabled (algorithm)) - continue; - - if (votes[(int) algorithm] > nvotes) { - nvotes = votes[(int) algorithm]; - chosen = algorithm; - } - } - - return chosen; - } - - internal RealAlgorithmIdentifier GetAlgorithmIdentifier (EncryptionAlgorithm algorithm) - { - switch (algorithm) { - case EncryptionAlgorithm.Aes256: - return new RealAlgorithmIdentifier (new Oid (CmsEnvelopedGenerator.Aes256Cbc)); - case EncryptionAlgorithm.Aes192: - return new RealAlgorithmIdentifier (new Oid (CmsEnvelopedGenerator.Aes192Cbc)); - case EncryptionAlgorithm.Aes128: - return new RealAlgorithmIdentifier (new Oid (CmsEnvelopedGenerator.Aes128Cbc)); - case EncryptionAlgorithm.TripleDes: - return new RealAlgorithmIdentifier (new Oid (CmsEnvelopedGenerator.DesEde3Cbc)); - case EncryptionAlgorithm.Des: - return new RealAlgorithmIdentifier (new Oid (SmimeCapability.DesCbc.Id)); - case EncryptionAlgorithm.RC2128: - return new RealAlgorithmIdentifier (new Oid (CmsEnvelopedGenerator.RC2Cbc), 128); - case EncryptionAlgorithm.RC264: - return new RealAlgorithmIdentifier (new Oid (CmsEnvelopedGenerator.RC2Cbc), 64); - case EncryptionAlgorithm.RC240: - return new RealAlgorithmIdentifier (new Oid (CmsEnvelopedGenerator.RC2Cbc), 40); - default: - throw new NotSupportedException (string.Format ("The {0} encryption algorithm is not supported by the {1}.", algorithm, GetType ().Name)); - } - } - - Stream Envelope (RealCmsRecipientCollection recipients, Stream content, EncryptionAlgorithm encryptionAlgorithm) - { - var contentInfo = new ContentInfo (ReadAllBytes (content)); - var algorithm = GetAlgorithmIdentifier (encryptionAlgorithm); - var envelopedData = new EnvelopedCms (contentInfo, algorithm); - - envelopedData.Encrypt (recipients); - - return new MemoryStream (envelopedData.Encode (), false); - } - - Stream Envelope (RealCmsRecipientCollection recipients, Stream content) - { - var algorithm = GetPreferredEncryptionAlgorithm (recipients); - - return Envelope (recipients, content, algorithm); - } - - Stream Envelope (CmsRecipientCollection recipients, Stream content) - { - var algorithm = GetPreferredEncryptionAlgorithm (recipients); - - return Envelope (GetCmsRecipients (recipients), content, algorithm); - } - - /// - /// Encrypts the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted content. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override ApplicationPkcs7Mime Encrypt (CmsRecipientCollection recipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - return new ApplicationPkcs7Mime (SecureMimeType.EnvelopedData, Envelope (recipients, content)); - } - - /// - /// Encrypts the specified content for the specified recipients. - /// - /// - /// Encrypts the specified content for the specified recipients. - /// - /// A new instance - /// containing the encrypted data. - /// The recipients. - /// The content. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// A certificate for one or more of the could not be found. - /// - /// - /// A certificate could not be found for one or more of the . - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override MimePart Encrypt (IEnumerable recipients, Stream content) - { - if (recipients == null) - throw new ArgumentNullException (nameof (recipients)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var real = GetCmsRecipients (recipients); - - return new ApplicationPkcs7Mime (SecureMimeType.EnvelopedData, Envelope (real, content)); - } - - /// - /// Decrypt the encrypted data. - /// - /// - /// Decrypt the encrypted data. - /// - /// The decrypted . - /// The encrypted data. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - public override MimeEntity Decrypt (Stream encryptedData, CancellationToken cancellationToken = default (CancellationToken)) - { - if (encryptedData == null) - throw new ArgumentNullException (nameof (encryptedData)); - - var enveloped = new EnvelopedCms (); - CryptographicException ce = null; - - enveloped.Decode (ReadAllBytes (encryptedData)); - - foreach (var recipient in enveloped.RecipientInfos) { - try { - enveloped.Decrypt (recipient); - ce = null; - break; - } catch (CryptographicException ex) { - ce = ex; - } - } - - if (ce != null) - throw ce; - - var decryptedData = enveloped.Encode (); - - var memory = new MemoryStream (decryptedData, false); - - return MimeEntity.Load (memory, true, cancellationToken); - } - - /// - /// Decrypts the specified encryptedData to an output stream. - /// - /// - /// Decrypts the specified encryptedData to an output stream. - /// - /// The encrypted data. - /// The decrypted data. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override void DecryptTo (Stream encryptedData, Stream decryptedData) - { - if (encryptedData == null) - throw new ArgumentNullException (nameof (encryptedData)); - - if (decryptedData == null) - throw new ArgumentNullException (nameof (decryptedData)); - - var enveloped = new EnvelopedCms (); - - enveloped.Decode (ReadAllBytes (encryptedData)); - enveloped.Decrypt (); - - var encoded = enveloped.Encode (); - - decryptedData.Write (encoded, 0, encoded.Length); - } - - /// - /// Import the specified certificate. - /// - /// - /// Import the specified certificate. - /// - /// The store to import the certificate into. - /// The certificate. - /// - /// is null. - /// - public void Import (StoreName storeName, X509Certificate2 certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - var store = new X509Store (storeName, StoreLocation); - - store.Open (OpenFlags.ReadWrite); - store.Add (certificate); - store.Close (); - } - - /// - /// Import the specified certificate. - /// - /// - /// Imports the specified certificate into the store. - /// - /// The certificate. - /// - /// is null. - /// - public void Import (X509Certificate2 certificate) - { - Import (StoreName.AddressBook, certificate); - } - - /// - /// Import the specified certificate. - /// - /// - /// Import the specified certificate. - /// - /// The store to import the certificate into. - /// The certificate. - /// - /// is null. - /// - public void Import (StoreName storeName, Org.BouncyCastle.X509.X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - Import (storeName, new X509Certificate2 (certificate.GetEncoded ())); - } - - /// - /// Import the specified certificate. - /// - /// - /// Imports the specified certificate into the store. - /// - /// The certificate. - /// - /// is null. - /// - public override void Import (Org.BouncyCastle.X509.X509Certificate certificate) - { - Import (StoreName.AddressBook, certificate); - } - - /// - /// Import the specified certificate revocation list. - /// - /// - /// Import the specified certificate revocation list. - /// - /// The certificate revocation list. - /// - /// is null. - /// - public override void Import (X509Crl crl) - { - if (crl == null) - throw new ArgumentNullException (nameof (crl)); - - foreach (Org.BouncyCastle.X509.X509Certificate certificate in crl.GetRevokedCertificates ()) - Import (StoreName.Disallowed, certificate); - } - - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// The raw certificate and key data. - /// The password to unlock the stream. - /// The storage flags to use when importing the certificate and private key. - /// - /// is null. - /// -or- - /// is null. - /// - public void Import (Stream stream, string password, X509KeyStorageFlags flags) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - var rawData = ReadAllBytes (stream); - var store = new X509Store (StoreName.My, StoreLocation); - var certs = new X509Certificate2Collection (); - - store.Open (OpenFlags.ReadWrite); - certs.Import (rawData, password, flags); - store.AddRange (certs); - store.Close (); - } - - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// - /// Imports certificates and keys from a pkcs12-encoded stream. - /// - /// The raw certificate and key data. - /// The password to unlock the stream. - /// - /// is null. - /// -or- - /// is null. - /// - public override void Import (Stream stream, string password) - { - Import (stream, password, DefaultKeyStorageFlags); - } - - /// - /// Exports the certificates for the specified mailboxes. - /// - /// - /// Exports the certificates for the specified mailboxes. - /// - /// A new instance containing - /// the exported keys. - /// The mailboxes. - /// - /// is null. - /// - /// - /// No mailboxes were specified. - /// - /// - /// A certificate for one or more of the could not be found. - /// - /// - /// An error occurred in the cryptographic message syntax subsystem. - /// - public override MimePart Export (IEnumerable mailboxes) - { - if (mailboxes == null) - throw new ArgumentNullException (nameof (mailboxes)); - - var certificates = new X509CertificateStore (); - int count = 0; - - foreach (var mailbox in mailboxes) { - var certificate = GetRecipientCertificate (mailbox); - - if (certificate != null) - certificates.Add (certificate.AsBouncyCastleCertificate ()); - - count++; - } - - if (count == 0) - throw new ArgumentException ("No mailboxes specified.", nameof (mailboxes)); - - var cms = new CmsSignedDataStreamGenerator (); - cms.AddCertificates (certificates); - - var memory = new MemoryBlockStream (); - cms.Open (memory).Dispose (); - memory.Position = 0; - - return new ApplicationPkcs7Mime (SecureMimeType.CertsOnly, memory); - } -#endregion - } -} diff --git a/src/MimeKit/Cryptography/WindowsSecureMimeDigitalCertificate.cs b/src/MimeKit/Cryptography/WindowsSecureMimeDigitalCertificate.cs deleted file mode 100644 index c95b823..0000000 --- a/src/MimeKit/Cryptography/WindowsSecureMimeDigitalCertificate.cs +++ /dev/null @@ -1,148 +0,0 @@ -// -// WindowsSecureMimeDigitalCertificate.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; - -namespace MimeKit.Cryptography { - /// - /// An S/MIME digital certificate. - /// - /// - /// An S/MIME digital certificate that is used with the . - /// - public class WindowsSecureMimeDigitalCertificate : IDigitalCertificate - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// An X.509 certificate. - /// - /// is null. - /// - public WindowsSecureMimeDigitalCertificate (X509Certificate2 certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - Certificate = certificate; - PublicKeyAlgorithm = certificate.GetPublicKeyAlgorithm (); - } - - /// - /// Get the X.509 certificate. - /// - /// - /// Gets the X.509 certificate. - /// - /// The certificate. - public X509Certificate2 Certificate { - get; private set; - } - -// /// -// /// Gets the chain status. -// /// -// /// The chain status. -// public X509ChainStatusFlags ChainStatus { -// get; internal set; -// } - - #region IDigitalCertificate implementation - - /// - /// Gets the public key algorithm supported by the certificate. - /// - /// - /// Gets the public key algorithm supported by the certificate. - /// - /// The public key algorithm. - public PublicKeyAlgorithm PublicKeyAlgorithm { - get; private set; - } - - /// - /// Gets the date that the certificate was created. - /// - /// - /// Gets the date that the certificate was created. - /// - /// The creation date. - public DateTime CreationDate { - get { return Certificate.NotBefore.ToUniversalTime (); } - } - - /// - /// Gets the expiration date of the certificate. - /// - /// - /// Gets the expiration date of the certificate. - /// - /// The expiration date. - public DateTime ExpirationDate { - get { return Certificate.NotAfter.ToUniversalTime (); } - } - - /// - /// Gets the fingerprint of the certificate. - /// - /// - /// Gets the fingerprint of the certificate. - /// - /// The fingerprint. - public string Fingerprint { - get { return Certificate.Thumbprint; } - } - - /// - /// Gets the email address of the owner of the certificate. - /// - /// - /// Gets the email address of the owner of the certificate. - /// - /// The email address. - public string Email { - get { return Certificate.GetNameInfo (X509NameType.EmailName, false); } - } - - /// - /// Gets the name of the owner of the certificate. - /// - /// - /// Gets the name of the owner of the certificate. - /// - /// The name of the owner. - public string Name { - get { return Certificate.GetNameInfo (X509NameType.SimpleName, false); } - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/WindowsSecureMimeDigitalSignature.cs b/src/MimeKit/Cryptography/WindowsSecureMimeDigitalSignature.cs deleted file mode 100644 index 7754ab3..0000000 --- a/src/MimeKit/Cryptography/WindowsSecureMimeDigitalSignature.cs +++ /dev/null @@ -1,212 +0,0 @@ -// -// WindowsSecureMimeDigitalSignature.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; -using System.Security.Cryptography.Pkcs; - -using Org.BouncyCastle.Asn1; - -using CmsAttributes = Org.BouncyCastle.Asn1.Cms.CmsAttributes; -using SmimeAttributes = Org.BouncyCastle.Asn1.Smime.SmimeAttributes; - -namespace MimeKit.Cryptography -{ - /// - /// An S/MIME digital signature. - /// - /// - /// An S/MIME digital signature that is used with the . - /// - public class WindowsSecureMimeDigitalSignature : IDigitalSignature - { - DigitalSignatureVerifyException vex; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The information about the signer. - /// - /// is null. - /// - public WindowsSecureMimeDigitalSignature (SignerInfo signerInfo) - { - if (signerInfo == null) - throw new ArgumentNullException (nameof (signerInfo)); - - SignerInfo = signerInfo; - - var algorithms = new List (); - DigestAlgorithm digestAlgo; - - if (signerInfo.SignedAttributes != null) { - for (int i = 0; i < signerInfo.SignedAttributes.Count; i++) { - if (signerInfo.SignedAttributes[i].Oid.Value == CmsAttributes.SigningTime.Id) { - var signingTime = signerInfo.SignedAttributes[i].Values[0] as Pkcs9SigningTime; - - if (signingTime != null) - CreationDate = signingTime.SigningTime; - } else if (signerInfo.SignedAttributes[i].Oid.Value == SmimeAttributes.SmimeCapabilities.Id) { - foreach (var value in signerInfo.SignedAttributes[i].Values) { - var sequences = (DerSequence) Asn1Object.FromByteArray (value.RawData); - - foreach (Asn1Sequence sequence in sequences) { - var identifier = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance (sequence); - EncryptionAlgorithm algorithm; - - if (BouncyCastleSecureMimeContext.TryGetEncryptionAlgorithm (identifier, out algorithm)) - algorithms.Add (algorithm); - } - } - } - } - } - - EncryptionAlgorithms = algorithms.ToArray (); - - if (WindowsSecureMimeContext.TryGetDigestAlgorithm (signerInfo.DigestAlgorithm, out digestAlgo)) - DigestAlgorithm = digestAlgo; - - SignerCertificate = new WindowsSecureMimeDigitalCertificate (signerInfo.Certificate); - } - - /// - /// Gets the signer info. - /// - /// - /// Gets the signer info. - /// - /// The signer info. - public SignerInfo SignerInfo { - get; private set; - } - - /// - /// Gets the list of encryption algorithms, in preferential order, - /// that the signer's client supports. - /// - /// - /// Gets the list of encryption algorithms, in preferential order, - /// that the signer's client supports. - /// - /// The S/MIME encryption algorithms. - public EncryptionAlgorithm[] EncryptionAlgorithms { - get; private set; - } - - #region IDigitalSignature implementation - - /// - /// Gets certificate used by the signer. - /// - /// - /// Gets certificate used by the signer. - /// - /// The signer's certificate. - public IDigitalCertificate SignerCertificate { - get; private set; - } - - /// - /// Gets the public key algorithm used for the signature. - /// - /// - /// Gets the public key algorithm used for the signature. - /// - /// The public key algorithm. - public PublicKeyAlgorithm PublicKeyAlgorithm { - get { return SignerCertificate != null ? SignerCertificate.PublicKeyAlgorithm : PublicKeyAlgorithm.None; } - } - - /// - /// Gets the digest algorithm used for the signature. - /// - /// - /// Gets the digest algorithm used for the signature. - /// - /// The digest algorithm. - public DigestAlgorithm DigestAlgorithm { - get; private set; - } - - /// - /// Gets the creation date of the digital signature. - /// - /// - /// Gets the creation date of the digital signature. - /// - /// The creation date in coordinated universal time (UTC). - public DateTime CreationDate { - get; private set; - } - - /// - /// Verifies the digital signature. - /// - /// - /// Verifies the digital signature. - /// - /// true if the signature is valid; otherwise false. - /// - /// An error verifying the signature has occurred. - /// - public bool Verify () - { - return Verify (false); - } - - /// - /// Verifies the digital signature. - /// - /// - /// Verifies the digital signature. - /// - /// true if only the signature itself should be verified; otherwise, both the signature and the certificate chain are validated. - /// true if the signature is valid; otherwise, false. - /// - /// An error verifying the signature has occurred. - /// - public bool Verify (bool verifySignatureOnly) - { - if (vex != null) - throw vex; - - try { - SignerInfo.CheckSignature (verifySignatureOnly); - return true; - } catch (Exception ex) { - var message = string.Format ("Failed to verify digital signature: {0}", ex.Message); - vex = new DigitalSignatureVerifyException (message, ex); - throw vex; - } - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/X509Certificate2Extensions.cs b/src/MimeKit/Cryptography/X509Certificate2Extensions.cs deleted file mode 100644 index d41365c..0000000 --- a/src/MimeKit/Cryptography/X509Certificate2Extensions.cs +++ /dev/null @@ -1,153 +0,0 @@ -// -// X509Certificate2Extensions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections.Generic; -using System.Security.Cryptography; - -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.X509; - -using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; - -namespace MimeKit.Cryptography -{ - /// - /// Extension methods for X509Certificate2. - /// - /// - /// Extension methods for X509Certificate2. - /// - public static class X509Certificate2Extensions - { - /// - /// Convert an X509Certificate2 into a BouncyCastle X509Certificate. - /// - /// - /// Converts an X509Certificate2 into a BouncyCastle X509Certificate. - /// - /// The bouncy castle certificate. - /// The certificate. - /// - /// is null. - /// - public static X509Certificate AsBouncyCastleCertificate (this X509Certificate2 certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - var rawData = certificate.GetRawCertData (); - - return new X509CertificateParser ().ReadCertificate (rawData); - } - - /// - /// Gets the public key algorithm for the certificate. - /// - /// - /// Gets the public key algorithm for the ceretificate. - /// - /// The public key algorithm. - /// The certificate. - /// - /// is null. - /// - public static PublicKeyAlgorithm GetPublicKeyAlgorithm (this X509Certificate2 certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - var identifier = certificate.GetKeyAlgorithm (); - var oid = new Oid (identifier); - - switch (oid.FriendlyName) { - case "DSA": return PublicKeyAlgorithm.Dsa; - case "RSA": return PublicKeyAlgorithm.RsaGeneral; - case "ECC": return PublicKeyAlgorithm.EllipticCurve; - case "DH": return PublicKeyAlgorithm.DiffieHellman; - default: return PublicKeyAlgorithm.None; - } - } - - static EncryptionAlgorithm[] DecodeEncryptionAlgorithms (byte[] rawData) - { - using (var memory = new MemoryStream (rawData, false)) { - using (var asn1 = new Asn1InputStream (memory)) { - var algorithms = new List (); - var sequence = asn1.ReadObject () as Asn1Sequence; - - if (sequence == null) - return null; - - for (int i = 0; i < sequence.Count; i++) { - var identifier = AlgorithmIdentifier.GetInstance (sequence[i]); - EncryptionAlgorithm algorithm; - - if (BouncyCastleSecureMimeContext.TryGetEncryptionAlgorithm (identifier, out algorithm)) - algorithms.Add (algorithm); - } - - return algorithms.ToArray (); - } - } - } - - /// - /// Get the encryption algorithms that can be used with an X.509 certificate. - /// - /// - /// Scans the X.509 certificate for the S/MIME capabilities extension. If found, - /// the supported encryption algorithms will be decoded and returned. - /// If no extension can be found, the - /// algorithm is returned. - /// - /// The encryption algorithms. - /// The X.509 certificate. - /// - /// is null. - /// - public static EncryptionAlgorithm[] GetEncryptionAlgorithms (this X509Certificate2 certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - foreach (var extension in certificate.Extensions) { - if (extension.Oid.Value == "1.2.840.113549.1.9.15") { - var algorithms = DecodeEncryptionAlgorithms (extension.RawData); - - if (algorithms != null) - return algorithms; - - break; - } - } - - return new EncryptionAlgorithm[] { EncryptionAlgorithm.TripleDes }; - } - } -} diff --git a/src/MimeKit/Cryptography/X509CertificateChain.cs b/src/MimeKit/Cryptography/X509CertificateChain.cs deleted file mode 100644 index 9193fe3..0000000 --- a/src/MimeKit/Cryptography/X509CertificateChain.cs +++ /dev/null @@ -1,381 +0,0 @@ -// -// X509CertificateChain.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Collections.Generic; - -using Org.BouncyCastle.X509; -using Org.BouncyCastle.X509.Store; - -namespace MimeKit.Cryptography { - /// - /// An X.509 certificate chain. - /// - /// - /// An X.509 certificate chain. - /// - public class X509CertificateChain : IList, IX509Store - { - readonly List certificates; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new X.509 certificate chain. - /// - public X509CertificateChain () - { - certificates = new List (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new X.509 certificate chain based on the specified collection of certificates. - /// - /// A collection of certificates. - /// - /// is null. - /// - public X509CertificateChain (IEnumerable collection) - { - certificates = new List (collection); - } - - #region IList implementation - - /// - /// Gets the index of the specified certificate within the chain. - /// - /// - /// Finds the index of the specified certificate, if it exists. - /// - /// The index of the specified certificate if found; otherwise -1. - /// The certificate to get the index of. - /// - /// is null. - /// - public int IndexOf (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - return certificates.IndexOf (certificate); - } - - /// - /// Inserts the certificate at the specified index. - /// - /// - /// Inserts the certificate at the specified index in the certificates. - /// - /// The index to insert the certificate. - /// The certificate. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void Insert (int index, X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - certificates.Insert (index, certificate); - } - - /// - /// Removes the certificate at the specified index. - /// - /// - /// Removes the certificate at the specified index. - /// - /// The index of the certificate to remove. - /// - /// is out of range. - /// - public void RemoveAt (int index) - { - if (index < 0 || index >= certificates.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - certificates.RemoveAt (index); - } - - /// - /// Gets or sets the certificate at the specified index. - /// - /// - /// Gets or sets the certificate at the specified index. - /// - /// The internet certificate at the specified index. - /// The index of the certificate to get or set. - /// - /// is null. - /// - /// - /// is out of range. - /// - public X509Certificate this [int index] { - get { return certificates[index]; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - certificates[index] = value; - } - } - #endregion - - #region ICollection implementation - - /// - /// Gets the number of certificates in the chain. - /// - /// - /// Indicates the number of certificates in the chain. - /// - /// The number of certificates. - public int Count { - get { return certificates.Count; } - } - - /// - /// Get a value indicating whether the is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Adds the specified certificate to the chain. - /// - /// - /// Adds the specified certificate to the chain. - /// - /// The certificate. - /// - /// is null. - /// - public void Add (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - certificates.Add (certificate); - } - - /// - /// Adds the specified range of certificates to the chain. - /// - /// - /// Adds the specified range of certificates to the chain. - /// - /// The certificates. - /// - /// is null. - /// - public void AddRange (IEnumerable certificates) - { - if (certificates == null) - throw new ArgumentNullException (nameof (certificates)); - - foreach (var certificate in certificates) - Add (certificate); - } - - /// - /// Clears the certificate chain. - /// - /// - /// Removes all of the certificates from the chain. - /// - public void Clear () - { - certificates.Clear (); - } - - /// - /// Checks if the chain contains the specified certificate. - /// - /// - /// Determines whether or not the certificate chain contains the specified certificate. - /// - /// true if the specified certificate exists; - /// otherwise false. - /// The certificate. - /// - /// is null. - /// - public bool Contains (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - return certificates.Contains (certificate); - } - - /// - /// Copies all of the certificates in the chain to the specified array. - /// - /// - /// Copies all of the certificates within the chain into the array, - /// starting at the specified array index. - /// - /// The array to copy the certificates to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (X509Certificate[] array, int arrayIndex) - { - certificates.CopyTo (array, arrayIndex); - } - - /// - /// Removes the specified certificate from the chain. - /// - /// - /// Removes the specified certificate from the chain. - /// - /// true if the certificate was removed; otherwise false. - /// The certificate. - /// - /// is null. - /// - public bool Remove (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - return certificates.Remove (certificate); - } - - /// - /// Removes the specified range of certificates from the chain. - /// - /// - /// Removes the specified range of certificates from the chain. - /// - /// The certificates. - /// - /// is null. - /// - public void RemoveRange (IEnumerable certificates) - { - if (certificates == null) - throw new ArgumentNullException (nameof (certificates)); - - foreach (var certificate in certificates) - Remove (certificate); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the list of certificates. - /// - /// - /// Gets an enumerator for the list of certificates. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return certificates.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the list of certificates. - /// - /// - /// Gets an enumerator for the list of certificates. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return certificates.GetEnumerator (); - } - - #endregion - - /// - /// Gets an enumerator of matching X.509 certificates based on the specified selector. - /// - /// - /// Gets an enumerator of matching X.509 certificates based on the specified selector. - /// - /// The matching certificates. - /// The match criteria. - public IEnumerable GetMatches (IX509Selector selector) - { - foreach (var certificate in certificates) { - if (selector == null || selector.Match (certificate)) - yield return certificate; - } - - yield break; - } - - #region IX509Store implementation - - /// - /// Gets a collection of matching X.509 certificates based on the specified selector. - /// - /// - /// Gets a collection of matching X.509 certificates based on the specified selector. - /// - /// The matching certificates. - /// The match criteria. - ICollection IX509Store.GetMatches (IX509Selector selector) - { - var matches = new List (); - - foreach (var certificate in GetMatches (selector)) - matches.Add (certificate); - - return matches; - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/X509CertificateDatabase.cs b/src/MimeKit/Cryptography/X509CertificateDatabase.cs deleted file mode 100644 index 2a64837..0000000 --- a/src/MimeKit/Cryptography/X509CertificateDatabase.cs +++ /dev/null @@ -1,985 +0,0 @@ -// -// X509CertificateDatabase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Data; -using System.Data.Common; -using System.Collections; -using System.Collections.Generic; - -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Asn1.BC; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.X509.Store; - -namespace MimeKit.Cryptography { - /// - /// An X.509 certificate database. - /// - /// - /// An X.509 certificate database is used for storing certificates, metadata related to the certificates - /// (such as encryption algorithms supported by the associated client), certificate revocation lists (CRLs), - /// and private keys. - /// - public abstract class X509CertificateDatabase : IX509CertificateDatabase - { - const X509CertificateRecordFields PrivateKeyFields = X509CertificateRecordFields.Certificate | X509CertificateRecordFields.PrivateKey; - static readonly DerObjectIdentifier DefaultEncryptionAlgorithm = BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc; - const int DefaultMinIterations = 1024; - const int DefaultSaltSize = 20; - - readonly char[] passwd; - - /// - /// Initialize a new instance of the class. - /// - /// - /// The password is used to encrypt and decrypt private keys in the database and cannot be null. - /// - /// The password used for encrypting and decrypting the private keys. - /// - /// is null. - /// - protected X509CertificateDatabase (string password) - { - if (password == null) - throw new ArgumentNullException (nameof (password)); - - EncryptionAlgorithm = DefaultEncryptionAlgorithm; - MinIterations = DefaultMinIterations; - SaltSize = DefaultSaltSize; - - passwd = password.ToCharArray (); - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~X509CertificateDatabase () - { - Dispose (false); - } - - /// - /// Gets or sets the algorithm used for encrypting the private keys. - /// - /// - /// The encryption algorithm should be one of the PBE (password-based encryption) algorithms - /// supported by Bouncy Castle. - /// The default algorithm is SHA-256 + AES256. - /// - /// The encryption algorithm. - protected DerObjectIdentifier EncryptionAlgorithm { - get; set; - } - - /// - /// Gets or sets the minimum iterations. - /// - /// - /// The default minimum number of iterations is 1024. - /// - /// The minimum iterations. - protected int MinIterations { - get; set; - } - - /// - /// Gets or sets the size of the salt. - /// - /// - /// The default salt size is 20. - /// - /// The size of the salt. - protected int SaltSize { - get; set; - } - - static int ReadBinaryBlob (DbDataReader reader, int column, ref byte[] buffer) - { -#if NETSTANDARD1_3 || NETSTANDARD1_6 - buffer = reader.GetFieldValue (column); - return (int) buffer.Length; -#else - long nread; - - // first, get the length of the buffer needed - if ((nread = reader.GetBytes (column, 0, null, 0, buffer.Length)) > buffer.Length) - Array.Resize (ref buffer, (int) nread); - - // read the certificate data - return (int) reader.GetBytes (column, 0, buffer, 0, (int) nread); -#endif - } - - static X509Certificate DecodeCertificate (DbDataReader reader, X509CertificateParser parser, int column, ref byte[] buffer) - { - int nread = ReadBinaryBlob (reader, column, ref buffer); - - using (var memory = new MemoryStream (buffer, 0, nread, false)) { - return parser.ReadCertificate (memory); - } - } - - static X509Crl DecodeX509Crl (DbDataReader reader, X509CrlParser parser, int column, ref byte[] buffer) - { - int nread = ReadBinaryBlob (reader, column, ref buffer); - - using (var memory = new MemoryStream (buffer, 0, nread, false)) { - return parser.ReadCrl (memory); - } - } - - byte[] EncryptAsymmetricKeyParameter (AsymmetricKeyParameter key) - { - var cipher = PbeUtilities.CreateEngine (EncryptionAlgorithm.Id) as IBufferedCipher; - var keyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo (key); - var random = new SecureRandom (); - var salt = new byte[SaltSize]; - - if (cipher == null) - throw new Exception ("Unknown encryption algorithm: " + EncryptionAlgorithm.Id); - - random.NextBytes (salt); - - var pbeParameters = PbeUtilities.GenerateAlgorithmParameters (EncryptionAlgorithm.Id, salt, MinIterations); - var algorithm = new AlgorithmIdentifier (EncryptionAlgorithm, pbeParameters); - var cipherParameters = PbeUtilities.GenerateCipherParameters (algorithm, passwd); - - if (cipherParameters == null) - throw new Exception ("BouncyCastle bug detected: Failed to generate cipher parameters."); - - cipher.Init (true, cipherParameters); - - var encoded = cipher.DoFinal (keyInfo.GetEncoded ()); - - var encrypted = new EncryptedPrivateKeyInfo (algorithm, encoded); - - return encrypted.GetEncoded (); - } - - AsymmetricKeyParameter DecryptAsymmetricKeyParameter (byte[] buffer, int length) - { - using (var memory = new MemoryStream (buffer, 0, length, false)) { - using (var asn1 = new Asn1InputStream (memory)) { - var sequence = asn1.ReadObject () as Asn1Sequence; - if (sequence == null) - return null; - - var encrypted = EncryptedPrivateKeyInfo.GetInstance (sequence); - var algorithm = encrypted.EncryptionAlgorithm; - var encoded = encrypted.GetEncryptedData (); - - var cipher = PbeUtilities.CreateEngine (algorithm) as IBufferedCipher; - if (cipher == null) - return null; - - var cipherParameters = PbeUtilities.GenerateCipherParameters (algorithm, passwd); - - if (cipherParameters == null) - throw new Exception ("BouncyCastle bug detected: Failed to generate cipher parameters."); - - cipher.Init (false, cipherParameters); - - var decrypted = cipher.DoFinal (encoded); - var keyInfo = PrivateKeyInfo.GetInstance (decrypted); - - return PrivateKeyFactory.CreateKey (keyInfo); - } - } - } - - AsymmetricKeyParameter DecodePrivateKey (DbDataReader reader, int column, ref byte[] buffer) - { - if (reader.IsDBNull (column)) - return null; - - int nread = ReadBinaryBlob (reader, column, ref buffer); - - return DecryptAsymmetricKeyParameter (buffer, nread); - } - - object EncodePrivateKey (AsymmetricKeyParameter key) - { - return key != null ? (object) EncryptAsymmetricKeyParameter (key) : DBNull.Value; - } - - static EncryptionAlgorithm[] DecodeEncryptionAlgorithms (DbDataReader reader, int column) - { - if (reader.IsDBNull (column)) - return null; - - var algorithms = new List (); - var values = reader.GetString (column); - - foreach (var token in values.Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { - EncryptionAlgorithm algorithm; - - if (Enum.TryParse (token.Trim (), true, out algorithm)) - algorithms.Add (algorithm); - } - - return algorithms.ToArray (); - } - - static object EncodeEncryptionAlgorithms (EncryptionAlgorithm[] algorithms) - { - if (algorithms == null || algorithms.Length == 0) - return DBNull.Value; - - var tokens = new string[algorithms.Length]; - for (int i = 0; i < algorithms.Length; i++) - tokens[i] = algorithms[i].ToString (); - - return string.Join (",", tokens); - } - - X509CertificateRecord LoadCertificateRecord (DbDataReader reader, X509CertificateParser parser, ref byte[] buffer) - { - var record = new X509CertificateRecord (); - - for (int i = 0; i < reader.FieldCount; i++) { - switch (reader.GetName (i).ToUpperInvariant ()) { - case "CERTIFICATE": - record.Certificate = DecodeCertificate (reader, parser, i, ref buffer); - break; - case "PRIVATEKEY": - record.PrivateKey = DecodePrivateKey (reader, i, ref buffer); - break; - case "ALGORITHMS": - record.Algorithms = DecodeEncryptionAlgorithms (reader, i); - break; - case "ALGORITHMSUPDATED": - record.AlgorithmsUpdated = DateTime.SpecifyKind (reader.GetDateTime (i), DateTimeKind.Utc); - break; - case "TRUSTED": - record.IsTrusted = reader.GetBoolean (i); - break; - case "ID": - record.Id = reader.GetInt32 (i); - break; - } - } - - return record; - } - - X509CrlRecord LoadCrlRecord (DbDataReader reader, X509CrlParser parser, ref byte[] buffer) - { - var record = new X509CrlRecord (); - - for (int i = 0; i < reader.FieldCount; i++) { - switch (reader.GetName (i).ToUpperInvariant ()) { - case "CRL": - record.Crl = DecodeX509Crl (reader, parser, i, ref buffer); - break; - case "THISUPDATE": - record.ThisUpdate = DateTime.SpecifyKind (reader.GetDateTime (i), DateTimeKind.Utc); - break; - case "NEXTUPDATE": - record.NextUpdate = DateTime.SpecifyKind (reader.GetDateTime (i), DateTimeKind.Utc); - break; - case "DELTA": - record.IsDelta = reader.GetBoolean (i); - break; - case "ID": - record.Id = reader.GetInt32 (i); - break; - } - } - - return record; - } - - /// - /// Gets the column names for the specified fields. - /// - /// - /// Gets the column names for the specified fields. - /// - /// The column names. - /// The fields. - protected static string[] GetColumnNames (X509CertificateRecordFields fields) - { - var columns = new List (); - - if ((fields & X509CertificateRecordFields.Id) != 0) - columns.Add ("ID"); - if ((fields & X509CertificateRecordFields.Trusted) != 0) - columns.Add ("TRUSTED"); - if ((fields & X509CertificateRecordFields.Algorithms) != 0) - columns.Add ("ALGORITHMS"); - if ((fields & X509CertificateRecordFields.AlgorithmsUpdated) != 0) - columns.Add ("ALGORITHMSUPDATED"); - if ((fields & X509CertificateRecordFields.Certificate) != 0) - columns.Add ("CERTIFICATE"); - if ((fields & X509CertificateRecordFields.PrivateKey) != 0) - columns.Add ("PRIVATEKEY"); - - return columns.ToArray (); - } - - /// - /// Gets the database command to select the record matching the specified certificate. - /// - /// - /// Gets the database command to select the record matching the specified certificate. - /// - /// The database command. - /// The certificate. - /// The fields to return. - protected abstract DbCommand GetSelectCommand (X509Certificate certificate, X509CertificateRecordFields fields); - - /// - /// Gets the database command to select the certificate records for the specified mailbox. - /// - /// - /// Gets the database command to select the certificate records for the specified mailbox. - /// - /// The database command. - /// The mailbox. - /// The date and time for which the certificate should be valid. - /// true if the certificate must have a private key; otherwise, false. - /// The fields to return. - protected abstract DbCommand GetSelectCommand (MailboxAddress mailbox, DateTime now, bool requirePrivateKey, X509CertificateRecordFields fields); - - /// - /// Gets the database command to select certificate records matching the specified selector. - /// - /// - /// Gets the database command to select certificate records matching the specified selector. - /// - /// The database command. - /// The certificate selector. - /// true if only trusted anchor certificates should be matched; otherwise, false. - /// true if the certificate must have a private key; otherwise, false. - /// The fields to return. - protected abstract DbCommand GetSelectCommand (IX509Selector selector, bool trustedAnchorsOnly, bool requirePrivateKey, X509CertificateRecordFields fields); - - /// - /// Gets the column names for the specified fields. - /// - /// - /// Gets the column names for the specified fields. - /// - /// The column names. - /// The fields. - protected static string[] GetColumnNames (X509CrlRecordFields fields) - { - var columns = new List (); - - if ((fields & X509CrlRecordFields.Id) != 0) - columns.Add ("ID"); - if ((fields & X509CrlRecordFields.IsDelta) != 0) - columns.Add ("DELTA"); - if ((fields & X509CrlRecordFields.IssuerName) != 0) - columns.Add ("ISSUERNAME"); - if ((fields & X509CrlRecordFields.ThisUpdate) != 0) - columns.Add ("THISUPDATE"); - if ((fields & X509CrlRecordFields.NextUpdate) != 0) - columns.Add ("NEXTUPDATE"); - if ((fields & X509CrlRecordFields.Crl) != 0) - columns.Add ("CRL"); - - return columns.ToArray (); - } - - /// - /// Gets the database command to select the CRL records matching the specified issuer. - /// - /// - /// Gets the database command to select the CRL records matching the specified issuer. - /// - /// The database command. - /// The issuer. - /// The fields to return. - protected abstract DbCommand GetSelectCommand (X509Name issuer, X509CrlRecordFields fields); - - /// - /// Gets the database command to select the record for the specified CRL. - /// - /// - /// Gets the database command to select the record for the specified CRL. - /// - /// The database command. - /// The X.509 CRL. - /// The fields to return. - protected abstract DbCommand GetSelectCommand (X509Crl crl, X509CrlRecordFields fields); - - /// - /// Gets the database command to select all CRLs in the table. - /// - /// - /// Gets the database command to select all CRLs in the table. - /// - /// The database command. - protected abstract DbCommand GetSelectAllCrlsCommand (); - - /// - /// Gets the database command to delete the specified certificate record. - /// - /// - /// Gets the database command to delete the specified certificate record. - /// - /// The database command. - /// The certificate record. - protected abstract DbCommand GetDeleteCommand (X509CertificateRecord record); - - /// - /// Gets the database command to delete the specified CRL record. - /// - /// - /// Gets the database command to delete the specified CRL record. - /// - /// The database command. - /// The record. - protected abstract DbCommand GetDeleteCommand (X509CrlRecord record); - - /// - /// Gets the value for the specified column. - /// - /// - /// Gets the value for the specified column. - /// - /// The value. - /// The certificate record. - /// The column name. - /// - /// is not a known column name. - /// - protected object GetValue (X509CertificateRecord record, string columnName) - { - switch (columnName) { - //case "ID": return record.Id; - case "BASICCONSTRAINTS": return record.BasicConstraints; - case "TRUSTED": return record.IsTrusted; - case "ANCHOR": return record.IsAnchor; - case "KEYUSAGE": return (int) record.KeyUsage; - case "NOTBEFORE": return record.NotBefore.ToUniversalTime (); - case "NOTAFTER": return record.NotAfter.ToUniversalTime (); - case "ISSUERNAME": return record.IssuerName; - case "SERIALNUMBER": return record.SerialNumber; - case "SUBJECTNAME": return record.SubjectName; - case "SUBJECTKEYIDENTIFIER": return record.SubjectKeyIdentifier?.AsHex (); - case "SUBJECTEMAIL": return record.SubjectEmail != null ? record.SubjectEmail.ToLowerInvariant () : string.Empty; - case "FINGERPRINT": return record.Fingerprint.ToLowerInvariant (); - case "ALGORITHMS": return EncodeEncryptionAlgorithms (record.Algorithms); - case "ALGORITHMSUPDATED": return record.AlgorithmsUpdated; - case "CERTIFICATE": return record.Certificate.GetEncoded (); - case "PRIVATEKEY": return EncodePrivateKey (record.PrivateKey); - default: throw new ArgumentException (string.Format ("Unknown column name: {0}", columnName), nameof (columnName)); - } - } - - /// - /// Gets the value for the specified column. - /// - /// - /// Gets the value for the specified column. - /// - /// The value. - /// The CRL record. - /// The column name. - /// - /// is not a known column name. - /// - protected static object GetValue (X509CrlRecord record, string columnName) - { - switch (columnName) { - //case "ID": return record.Id; - case "DELTA": return record.IsDelta; - case "ISSUERNAME": return record.IssuerName; - case "THISUPDATE": return record.ThisUpdate; - case "NEXTUPDATE": return record.NextUpdate; - case "CRL": return record.Crl.GetEncoded (); - default: throw new ArgumentException (string.Format ("Unknown column name: {0}", columnName), nameof (columnName)); - } - } - - /// - /// Gets the database command to insert the specified certificate record. - /// - /// - /// Gets the database command to insert the specified certificate record. - /// - /// The database command. - /// The certificate record. - protected abstract DbCommand GetInsertCommand (X509CertificateRecord record); - - /// - /// Gets the database command to insert the specified CRL record. - /// - /// - /// Gets the database command to insert the specified CRL record. - /// - /// The database command. - /// The CRL record. - protected abstract DbCommand GetInsertCommand (X509CrlRecord record); - - /// - /// Gets the database command to update the specified record. - /// - /// - /// Gets the database command to update the specified record. - /// - /// The database command. - /// The certificate record. - /// The fields to update. - protected abstract DbCommand GetUpdateCommand (X509CertificateRecord record, X509CertificateRecordFields fields); - - /// - /// Gets the database command to update the specified CRL record. - /// - /// - /// Gets the database command to update the specified CRL record. - /// - /// The database command. - /// The CRL record. - protected abstract DbCommand GetUpdateCommand (X509CrlRecord record); - - /// - /// Find the specified certificate. - /// - /// - /// Searches the database for the specified certificate, returning the matching - /// record with the desired fields populated. - /// - /// The matching record if found; otherwise null. - /// The certificate. - /// The desired fields. - /// - /// is null. - /// - public X509CertificateRecord Find (X509Certificate certificate, X509CertificateRecordFields fields) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - using (var command = GetSelectCommand (certificate, fields)) { - using (var reader = command.ExecuteReader ()) { - if (reader.Read ()) { - var parser = new X509CertificateParser (); - var buffer = new byte[4096]; - - return LoadCertificateRecord (reader, parser, ref buffer); - } - } - } - - return null; - } - - /// - /// Finds the certificates matching the specified selector. - /// - /// - /// Searches the database for certificates matching the selector, returning all - /// matching certificates. - /// - /// The matching certificates. - /// The match selector or null to return all certificates. - public IEnumerable FindCertificates (IX509Selector selector) - { - using (var command = GetSelectCommand (selector, false, false, X509CertificateRecordFields.Certificate)) { - using (var reader = command.ExecuteReader ()) { - var parser = new X509CertificateParser (); - var buffer = new byte[4096]; - - while (reader.Read ()) { - var record = LoadCertificateRecord (reader, parser, ref buffer); - if (selector == null || selector.Match (record.Certificate)) - yield return record.Certificate; - } - } - } - - yield break; - } - - /// - /// Finds the private keys matching the specified selector. - /// - /// - /// Searches the database for certificate records matching the selector, returning the - /// private keys for each matching record. - /// - /// The matching certificates. - /// The match selector or null to return all private keys. - public IEnumerable FindPrivateKeys (IX509Selector selector) - { - using (var command = GetSelectCommand (selector, false, true, PrivateKeyFields)) { - using (var reader = command.ExecuteReader ()) { - var parser = new X509CertificateParser (); - var buffer = new byte[4096]; - - while (reader.Read ()) { - var record = LoadCertificateRecord (reader, parser, ref buffer); - - if (selector == null || selector.Match (record.Certificate)) - yield return record.PrivateKey; - } - } - } - - yield break; - } - - /// - /// Finds the certificate records for the specified mailbox. - /// - /// - /// Searches the database for certificates matching the specified mailbox that are valid - /// for the date and time specified, returning all matching records populated with the - /// desired fields. - /// - /// The matching certificate records populated with the desired fields. - /// The mailbox. - /// The date and time. - /// true if a private key is required. - /// The desired fields. - /// - /// is null. - /// - public IEnumerable Find (MailboxAddress mailbox, DateTime now, bool requirePrivateKey, X509CertificateRecordFields fields) - { - if (mailbox == null) - throw new ArgumentNullException (nameof (mailbox)); - - using (var command = GetSelectCommand (mailbox, now, requirePrivateKey, fields)) { - using (var reader = command.ExecuteReader ()) { - var parser = new X509CertificateParser (); - var buffer = new byte[4096]; - - while (reader.Read ()) { - yield return LoadCertificateRecord (reader, parser, ref buffer); - } - } - } - - yield break; - } - - /// - /// Finds the certificate records matching the specified selector. - /// - /// - /// Searches the database for certificate records matching the selector, returning all - /// of the matching records populated with the desired fields. - /// - /// The matching certificate records populated with the desired fields. - /// The match selector or null to match all certificates. - /// true if only trusted anchor certificates should be returned. - /// The desired fields. - public IEnumerable Find (IX509Selector selector, bool trustedAnchorsOnly, X509CertificateRecordFields fields) - { - using (var command = GetSelectCommand (selector, trustedAnchorsOnly, false, fields | X509CertificateRecordFields.Certificate)) { - using (var reader = command.ExecuteReader ()) { - var parser = new X509CertificateParser (); - var buffer = new byte[4096]; - - while (reader.Read ()) { - var record = LoadCertificateRecord (reader, parser, ref buffer); - - if (selector == null || selector.Match (record.Certificate)) - yield return record; - } - } - } - - yield break; - } - - /// - /// Add the specified certificate record. - /// - /// - /// Adds the specified certificate record to the database. - /// - /// The certificate record. - /// - /// is null. - /// - public void Add (X509CertificateRecord record) - { - if (record == null) - throw new ArgumentNullException (nameof (record)); - - using (var command = GetInsertCommand (record)) - command.ExecuteNonQuery (); - } - - /// - /// Remove the specified certificate record. - /// - /// - /// Removes the specified certificate record from the database. - /// - /// The certificate record. - /// - /// is null. - /// - public void Remove (X509CertificateRecord record) - { - if (record == null) - throw new ArgumentNullException (nameof (record)); - - using (var command = GetDeleteCommand (record)) - command.ExecuteNonQuery (); - } - - /// - /// Update the specified certificate record. - /// - /// - /// Updates the specified fields of the record in the database. - /// - /// The certificate record. - /// The fields to update. - /// - /// is null. - /// - public void Update (X509CertificateRecord record, X509CertificateRecordFields fields) - { - if (record == null) - throw new ArgumentNullException (nameof (record)); - - using (var command = GetUpdateCommand (record, fields)) - command.ExecuteNonQuery (); - } - - /// - /// Finds the CRL records for the specified issuer. - /// - /// - /// Searches the database for CRL records matching the specified issuer, returning - /// all matching records populated with the desired fields. - /// - /// The matching CRL records populated with the desired fields. - /// The issuer. - /// The desired fields. - /// - /// is null. - /// - public IEnumerable Find (X509Name issuer, X509CrlRecordFields fields) - { - if (issuer == null) - throw new ArgumentNullException (nameof (issuer)); - - using (var command = GetSelectCommand (issuer, fields)) { - using (var reader = command.ExecuteReader ()) { - var parser = new X509CrlParser (); - var buffer = new byte[4096]; - - while (reader.Read ()) { - yield return LoadCrlRecord (reader, parser, ref buffer); - } - } - } - - yield break; - } - - /// - /// Finds the specified certificate revocation list. - /// - /// - /// Searches the database for the specified CRL, returning the matching record with - /// the desired fields populated. - /// - /// The matching record if found; otherwise null. - /// The certificate revocation list. - /// The desired fields. - /// - /// is null. - /// - public X509CrlRecord Find (X509Crl crl, X509CrlRecordFields fields) - { - if (crl == null) - throw new ArgumentNullException (nameof (crl)); - - using (var command = GetSelectCommand (crl, fields)) { - using (var reader = command.ExecuteReader ()) { - if (reader.Read ()) { - var parser = new X509CrlParser (); - var buffer = new byte[4096]; - - return LoadCrlRecord (reader, parser, ref buffer); - } - } - } - - return null; - } - - /// - /// Add the specified CRL record. - /// - /// - /// Adds the specified CRL record to the database. - /// - /// The CRL record. - /// - /// is null. - /// - public void Add (X509CrlRecord record) - { - if (record == null) - throw new ArgumentNullException (nameof (record)); - - using (var command = GetInsertCommand (record)) - command.ExecuteNonQuery (); - } - - /// - /// Remove the specified CRL record. - /// - /// - /// Removes the specified CRL record from the database. - /// - /// The CRL record. - /// - /// is null. - /// - public void Remove (X509CrlRecord record) - { - if (record == null) - throw new ArgumentNullException (nameof (record)); - - using (var command = GetDeleteCommand (record)) - command.ExecuteNonQuery (); - } - - /// - /// Update the specified CRL record. - /// - /// - /// Updates the specified fields of the record in the database. - /// - /// The CRL record. - /// - /// is null. - /// - public void Update (X509CrlRecord record) - { - if (record == null) - throw new ArgumentNullException (nameof (record)); - - using (var command = GetUpdateCommand (record)) - command.ExecuteNonQuery (); - } - - /// - /// Gets a certificate revocation list store. - /// - /// - /// Gets a certificate revocation list store. - /// - /// A certificate revocation list store. - public IX509Store GetCrlStore () - { - var crls = new List (); - - using (var command = GetSelectAllCrlsCommand ()) { - using (var reader = command.ExecuteReader ()) { - var parser = new X509CrlParser (); - var buffer = new byte[4096]; - - while (reader.Read ()) { - var record = LoadCrlRecord (reader, parser, ref buffer); - crls.Add (record.Crl); - } - } - } - - return X509StoreFactory.Create ("Crl/Collection", new X509CollectionStoreParameters (crls)); - } - -#region IX509Store implementation - - /// - /// Gets a collection of matching certificates matching the specified selector. - /// - /// - /// Gets a collection of matching certificates matching the specified selector. - /// - /// The matching certificates. - /// The match criteria. - ICollection IX509Store.GetMatches (IX509Selector selector) - { - return new List (FindCertificates (selector)); - } - -#endregion - -#region IDisposable implementation - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected virtual void Dispose (bool disposing) - { - if (passwd != null) { - for (int i = 0; i < passwd.Length; i++) - passwd[i] = '\0'; - } - } - - /// - /// Releases all resource used by the object. - /// - /// Call when you are finished using the - /// . The method leaves the - /// in an unusable state. After calling - /// , you must release all references to the - /// so the garbage collector can reclaim the memory that - /// the was occupying. - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - } - -#endregion - } -} diff --git a/src/MimeKit/Cryptography/X509CertificateRecord.cs b/src/MimeKit/Cryptography/X509CertificateRecord.cs deleted file mode 100644 index fa971db..0000000 --- a/src/MimeKit/Cryptography/X509CertificateRecord.cs +++ /dev/null @@ -1,315 +0,0 @@ -// -// X509CertificateRecord.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Asn1.X509; - -namespace MimeKit.Cryptography { - /// - /// X.509 certificate record fields. - /// - /// - /// The record fields are used when querying the - /// for certificates. - /// - [Flags] - public enum X509CertificateRecordFields { - /// - /// The "id" field is typically just the ROWID in the database. - /// - Id = 1 << 0, - - /// - /// The "trusted" field is a boolean value indicating whether the certificate - /// is trusted. - /// - Trusted = 1 << 1, - - /// - /// The "algorithms" field is used for storing the last known list of - /// values that are supported by the - /// client associated with the certificate. - /// - Algorithms = 1 << 3, - - /// - /// The "algorithms updated" field is used to store the timestamp of the - /// most recent update to the Algorithms field. - /// - AlgorithmsUpdated = 1 << 4, - - /// - /// The "certificate" field is sued for storing the binary data of the actual - /// certificate. - /// - Certificate = 1 << 5, - - /// - /// The "private key" field is used to store the encrypted binary data of the - /// private key associated with the certificate, if available. - /// - PrivateKey = 1 << 6, - } - - /// - /// An X.509 certificate record. - /// - /// - /// Represents an X.509 certificate record loaded from a database. - /// - public class X509CertificateRecord - { - /// - /// Gets the identifier. - /// - /// - /// The id is typically the ROWID of the certificate in the database and is not - /// generally useful outside of the internals of the database implementation. - /// - /// The identifier. - public int Id { get; internal set; } - - /// - /// Gets the basic constraints of the certificate. - /// - /// - /// Gets the basic constraints of the certificate. - /// - /// The basic constraints of the certificate. - public int BasicConstraints { get { return Certificate.GetBasicConstraints (); } } - - /// - /// Gets or sets a value indicating whether the certificate is trusted. - /// - /// - /// Indiciates whether or not the certificate is trusted. - /// - /// true if the certificate is trusted; otherwise, false. - public bool IsTrusted { get; set; } - - /// - /// Gets whether or not the certificate is an anchor. - /// - /// - /// Gets whether or not the certificate is an anchor. - /// - /// true if the certificate is an anchor; otherwise, false. - public bool IsAnchor { get { return Certificate.IsSelfSigned (); } } - - /// - /// Gets the key usage flags for the certificate. - /// - /// - /// Gets the key usage flags for the certificate. - /// - /// The X.509 key usage. - public X509KeyUsageFlags KeyUsage { get { return Certificate.GetKeyUsageFlags (); } } - - /// - /// Gets the starting date and time where the certificate is valid. - /// - /// - /// Gets the starting date and time where the certificate is valid. - /// - /// The date and time in coordinated universal time (UTC). - public DateTime NotBefore { get { return Certificate.NotBefore.ToUniversalTime (); } } - - /// - /// Gets the end date and time where the certificate is valid. - /// - /// - /// Gets the end date and time where the certificate is valid. - /// - /// The date and time in coordinated universal time (UTC). - public DateTime NotAfter { get { return Certificate.NotAfter.ToUniversalTime (); } } - - /// - /// Gets the certificate's issuer name. - /// - /// - /// Gets the certificate's issuer name. - /// - /// The certificate's issuer name. - public string IssuerName { get { return Certificate.IssuerDN.ToString (); } } - - /// - /// Gets the serial number of the certificate. - /// - /// - /// Gets the serial number of the certificate. - /// - /// The serial number. - public string SerialNumber { get { return Certificate.SerialNumber.ToString (); } } - - /// - /// Gets the certificate's subject name. - /// - /// - /// Gets the certificate's subject name. - /// - /// The certificate's subject name. - public string SubjectName { get { return Certificate.SubjectDN.ToString (); } } - - /// - /// Gets the certificate's subject key identifier. - /// - /// - /// Gets the certificate's subject key identifier. - /// - /// The certificate's subject key identifier. - public byte[] SubjectKeyIdentifier { - get { - var subjectKeyIdentifier = Certificate.GetExtensionValue (X509Extensions.SubjectKeyIdentifier); - - if (subjectKeyIdentifier != null) - subjectKeyIdentifier = (Asn1OctetString) Asn1Object.FromByteArray (subjectKeyIdentifier.GetOctets ()); - - return subjectKeyIdentifier?.GetOctets (); - } - } - - /// - /// Gets the subject email address. - /// - /// - /// Gets the subject email address. - /// - /// The subject email address. - public string SubjectEmail { get { return Certificate.GetSubjectEmailAddress (); } } - - /// - /// Gets the fingerprint of the certificate. - /// - /// - /// Gets the fingerprint of the certificate. - /// - /// The fingerprint. - public string Fingerprint { get { return Certificate.GetFingerprint (); } } - - /// - /// Gets or sets the encryption algorithm capabilities. - /// - /// - /// Gets or sets the encryption algorithm capabilities. - /// - /// The encryption algorithms. - public EncryptionAlgorithm[] Algorithms { get; set; } - - /// - /// Gets or sets the date when the algorithms were last updated. - /// - /// - /// Gets or sets the date when the algorithms were last updated. - /// - /// The date the algorithms were updated. - public DateTime AlgorithmsUpdated { get; set; } - - /// - /// Gets the certificate. - /// - /// - /// Gets the certificate. - /// - /// The certificate. - public X509Certificate Certificate { get; internal set; } - - /// - /// Gets the private key. - /// - /// - /// Gets the private key. - /// - /// The private key. - public AsymmetricKeyParameter PrivateKey { get; set; } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new certificate record with a private key for storing in a - /// . - /// - /// The certificate. - /// The private key. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a private key. - /// - public X509CertificateRecord (X509Certificate certificate, AsymmetricKeyParameter key) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - if (key == null) - throw new ArgumentNullException (nameof (key)); - - if (!key.IsPrivate) - throw new ArgumentException ("The key must be private.", nameof (key)); - - AlgorithmsUpdated = DateTime.MinValue; - Certificate = certificate; - PrivateKey = key; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new certificate record for storing in a . - /// - /// The certificate. - /// - /// is null. - /// - public X509CertificateRecord (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - AlgorithmsUpdated = DateTime.MinValue; - Certificate = certificate; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is only meant to be used by implementors of - /// when loading records from the database. - /// - public X509CertificateRecord () - { - } - } -} diff --git a/src/MimeKit/Cryptography/X509CertificateStore.cs b/src/MimeKit/Cryptography/X509CertificateStore.cs deleted file mode 100644 index 6bdd6e0..0000000 --- a/src/MimeKit/Cryptography/X509CertificateStore.cs +++ /dev/null @@ -1,544 +0,0 @@ -// -// X509CertificateStore.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections; -using System.Collections.Generic; - -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.X509.Store; - -namespace MimeKit.Cryptography { - /// - /// A store for X.509 certificates and keys. - /// - /// - /// A store for X.509 certificates and keys. - /// - public class X509CertificateStore : IX509Store - { - readonly Dictionary keys; - readonly HashSet unique; - readonly List certs; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public X509CertificateStore () - { - keys = new Dictionary (); - unique = new HashSet (); - certs = new List (); - } - - /// - /// Enumerates the certificates currently in the store. - /// - /// - /// Enumerates the certificates currently in the store. - /// - /// The certificates. - public IEnumerable Certificates { - get { return certs; } - } - - /// - /// Gets the private key for the specified certificate. - /// - /// - /// Gets the private key for the specified certificate, if it exists. - /// - /// The private key on success; otherwise null. - /// The certificate. - public AsymmetricKeyParameter GetPrivateKey (X509Certificate certificate) - { - AsymmetricKeyParameter key; - - if (!keys.TryGetValue (certificate, out key)) - return null; - - return key; - } - - /// - /// Adds the specified certificate to the store. - /// - /// - /// Adds the specified certificate to the store. - /// - /// The certificate. - /// - /// is null. - /// - public void Add (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - if (unique.Add (certificate)) - certs.Add (certificate); - } - - /// - /// Adds the specified range of certificates to the store. - /// - /// - /// Adds the specified range of certificates to the store. - /// - /// The certificates. - /// - /// is null. - /// - public void AddRange (IEnumerable certificates) - { - if (certificates == null) - throw new ArgumentNullException (nameof (certificates)); - - foreach (var certificate in certificates) { - if (unique.Add (certificate)) - certs.Add (certificate); - } - } - - /// - /// Removes the specified certificate from the store. - /// - /// - /// Removes the specified certificate from the store. - /// - /// The certificate. - /// - /// is null. - /// - public void Remove (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException (nameof (certificate)); - - if (unique.Remove (certificate)) - certs.Remove (certificate); - } - - /// - /// Removes the specified range of certificates from the store. - /// - /// - /// Removes the specified range of certificates from the store. - /// - /// The certificates. - /// - /// is null. - /// - public void RemoveRange (IEnumerable certificates) - { - if (certificates == null) - throw new ArgumentNullException (nameof (certificates)); - - foreach (var certificate in certificates) { - if (unique.Remove (certificate)) - certs.Remove (certificate); - } - } - - /// - /// Imports the certificate(s) from the specified stream. - /// - /// - /// Imports the certificate(s) from the specified stream. - /// - /// The stream to import. - /// - /// is null. - /// - /// - /// An error occurred reading the stream. - /// - public void Import (Stream stream) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new X509CertificateParser (); - - foreach (X509Certificate certificate in parser.ReadCertificates (stream)) { - if (unique.Add (certificate)) - certs.Add (certificate); - } - } - - /// - /// Imports the certificate(s) from the specified file. - /// - /// - /// Imports the certificate(s) from the specified file. - /// - /// The name of the file to import. - /// - /// is null. - /// - /// - /// The specified file could not be found. - /// - /// - /// An error occurred reading the file. - /// - public void Import (string fileName) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - Import (stream); - } - - /// - /// Imports the certificate(s) from the specified byte array. - /// - /// - /// Imports the certificate(s) from the specified byte array. - /// - /// The raw certificate data. - /// - /// is null. - /// - public void Import (byte[] rawData) - { - if (rawData == null) - throw new ArgumentNullException (nameof (rawData)); - - using (var stream = new MemoryStream (rawData, false)) - Import (stream); - } - - /// - /// Imports certificates and private keys from the specified stream. - /// - /// - /// Imports certificates and private keys from the specified pkcs12 stream. - /// - /// The stream to import. - /// The password to unlock the stream. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred reading the stream. - /// - public void Import (Stream stream, string password) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ()); - - foreach (string alias in pkcs12.Aliases) { - if (pkcs12.IsKeyEntry (alias)) { - var chain = pkcs12.GetCertificateChain (alias); - var entry = pkcs12.GetKey (alias); - - for (int i = 0; i < chain.Length; i++) { - if (unique.Add (chain[i].Certificate)) - certs.Add (chain[i].Certificate); - } - - if (entry.Key.IsPrivate) - keys.Add (chain[0].Certificate, entry.Key); - } else if (pkcs12.IsCertificateEntry (alias)) { - var entry = pkcs12.GetCertificate (alias); - - if (unique.Add (entry.Certificate)) - certs.Add (entry.Certificate); - } - } - } - - /// - /// Imports certificates and private keys from the specified file. - /// - /// - /// Imports certificates and private keys from the specified pkcs12 stream. - /// - /// The name of the file to import. - /// The password to unlock the file. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The specified file could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// An error occurred reading the file. - /// - public void Import (string fileName, string password) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - Import (stream, password); - } - - /// - /// Imports certificates and private keys from the specified byte array. - /// - /// - /// Imports certificates and private keys from the specified pkcs12 stream. - /// - /// The raw certificate data. - /// The password to unlock the raw data. - /// - /// is null. - /// -or- - /// is null. - /// - public void Import (byte[] rawData, string password) - { - if (rawData == null) - throw new ArgumentNullException (nameof (rawData)); - - using (var stream = new MemoryStream (rawData, false)) - Import (stream, password); - } - - /// - /// Exports the certificates to an unencrypted stream. - /// - /// - /// Exports the certificates to an unencrypted stream. - /// - /// The output stream. - /// - /// is null. - /// - /// - /// An error occurred while writing to the stream. - /// - public void Export (Stream stream) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - foreach (var certificate in certs) { - var encoded = certificate.GetEncoded (); - stream.Write (encoded, 0, encoded.Length); - } - } - - /// - /// Exports the certificates to an unencrypted file. - /// - /// - /// Exports the certificates to an unencrypted file. - /// - /// The file path to write to. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The specified path exceeds the maximum allowed path length of the system. - /// - /// - /// A directory in the specified path does not exist. - /// - /// - /// The user does not have access to create the specified file. - /// - /// - /// An error occurred while writing to the stream. - /// - public void Export (string fileName) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var file = File.Create (fileName)) - Export (file); - } - - /// - /// Exports the specified stream and password to a pkcs12 encrypted file. - /// - /// - /// Exports the specified stream and password to a pkcs12 encrypted file. - /// - /// The output stream. - /// The password to use to lock the private keys. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// An error occurred while writing to the stream. - /// - public void Export (Stream stream, string password) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - var store = new Pkcs12Store (); - foreach (var certificate in certs) { - if (keys.ContainsKey (certificate)) - continue; - - var alias = certificate.GetCommonName (); - - if (alias == null) - continue; - - var entry = new X509CertificateEntry (certificate); - - store.SetCertificateEntry (alias, entry); - } - - foreach (var kvp in keys) { - var alias = kvp.Key.GetCommonName (); - - if (alias == null) - continue; - - var entry = new AsymmetricKeyEntry (kvp.Value); - var cert = new X509CertificateEntry (kvp.Key); - var chain = new List (); - - chain.Add (cert); - - store.SetKeyEntry (alias, entry, chain.ToArray ()); - } - - store.Save (stream, password.ToCharArray (), new SecureRandom ()); - } - - /// - /// Exports the specified stream and password to a pkcs12 encrypted file. - /// - /// - /// Exports the specified stream and password to a pkcs12 encrypted file. - /// - /// The file path to write to. - /// The password to use to lock the private keys. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The specified path exceeds the maximum allowed path length of the system. - /// - /// - /// A directory in the specified path does not exist. - /// - /// - /// The user does not have access to create the specified file. - /// - /// - /// An error occurred while writing to the stream. - /// - public void Export (string fileName, string password) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - if (password == null) - throw new ArgumentNullException (nameof (password)); - - using (var file = File.Create (fileName)) - Export (file, password); - } - - /// - /// Gets an enumerator of matching X.509 certificates based on the specified selector. - /// - /// - /// Gets an enumerator of matching X.509 certificates based on the specified selector. - /// - /// The matching certificates. - /// The match criteria. - public IEnumerable GetMatches (IX509Selector selector) - { - foreach (var certificate in certs) { - if (selector == null || selector.Match (certificate)) - yield return certificate; - } - - yield break; - } - - #region IX509Store implementation - - /// - /// Gets a collection of matching X.509 certificates based on the specified selector. - /// - /// - /// Gets a collection of matching X.509 certificates based on the specified selector. - /// - /// The matching certificates. - /// The match criteria. - ICollection IX509Store.GetMatches (IX509Selector selector) - { - var matches = new List (); - - foreach (var certificate in GetMatches (selector)) - matches.Add (certificate); - - return matches; - } - - #endregion - } -} diff --git a/src/MimeKit/Cryptography/X509CrlRecord.cs b/src/MimeKit/Cryptography/X509CrlRecord.cs deleted file mode 100644 index 1bb17b6..0000000 --- a/src/MimeKit/Cryptography/X509CrlRecord.cs +++ /dev/null @@ -1,172 +0,0 @@ -// -// X509CrlRecord.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using Org.BouncyCastle.X509; - -namespace MimeKit.Cryptography { - /// - /// X.509 certificate revocation list record fields. - /// - /// - /// The record fields are used when querying the - /// for certificate revocation lists. - /// - [Flags] - public enum X509CrlRecordFields { - /// - /// The "id" field is typically just the ROWID in the database. - /// - Id = 1 << 0, - - /// - /// The "delta" field is a boolean value indicating whether the certificate - /// revocation list is a delta. - /// - IsDelta = 1 << 1, - - /// - /// The "issuer name" field stores the issuer name of the certificate revocation list. - /// - IssuerName = 1 << 2, - - /// - /// The "this update" field stores the date and time of the most recent update. - /// - ThisUpdate = 1 << 3, - - /// - /// The "next update" field stores the date and time of the next scheduled update. - /// - NextUpdate = 1 << 4, - - /// - /// The "crl" field stores the raw binary data of the certificate revocation list. - /// - Crl = 1 << 5, - } - - /// - /// An X.509 certificate revocation list (CRL) record. - /// - /// - /// Represents an X.509 certificate revocation list record loaded from a database. - /// - public class X509CrlRecord - { - /// - /// Gets the identifier. - /// - /// - /// The id is typically the ROWID of the certificate revocation list in the - /// database and is not generally useful outside of the internals of the - /// database implementation. - /// - /// The identifier. - public int Id { get; internal set; } - - /// - /// Gets whether or not this certificate revocation list is a delta. - /// - /// - /// Indicates whether or not this certificate revocation list is a delta. - /// - /// true if th crl is delta; otherwise, false. - public bool IsDelta { get; internal set; } - - /// - /// Gets the issuer name of the certificate revocation list. - /// - /// - /// Gets the issuer name of the certificate revocation list. - /// - /// The issuer's name. - public string IssuerName { get; internal set; } - - /// - /// Gets the date and time of the most recent update. - /// - /// - /// Gets the date and time of the most recent update. - /// - /// The date and time. - public DateTime ThisUpdate { get; internal set; } - - /// - /// Gets the date and time when the next CRL update will be published. - /// - /// - /// Gets the date and time when the next CRL update will be published. - /// - /// The date and time. - public DateTime NextUpdate { get; internal set; } - - /// - /// Gets the certificate revocation list. - /// - /// - /// Gets the certificate revocation list. - /// - /// The certificate revocation list. - public X509Crl Crl { get; set; } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new CRL record for storing in a . - /// - /// The certificate revocation list. - /// - /// is null. - /// - public X509CrlRecord (X509Crl crl) - { - if (crl == null) - throw new ArgumentNullException (nameof (crl)); - - if (crl.NextUpdate != null) - NextUpdate = crl.NextUpdate.Value.ToUniversalTime (); - - IssuerName = crl.IssuerDN.ToString (); - ThisUpdate = crl.ThisUpdate.ToUniversalTime (); - IsDelta = crl.IsDelta (); - Crl = crl; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is only meant to be used by implementors of - /// when loading records from the database. - /// - public X509CrlRecord () - { - } - } -} diff --git a/src/MimeKit/Cryptography/X509KeyUsageFlags.cs b/src/MimeKit/Cryptography/X509KeyUsageFlags.cs deleted file mode 100644 index 689924a..0000000 --- a/src/MimeKit/Cryptography/X509KeyUsageFlags.cs +++ /dev/null @@ -1,121 +0,0 @@ -// -// X509KeyUsageFlags.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Cryptography { - /// - /// X.509 key usage flags. - /// - /// - /// The X.509 Key Usage Flags can be used to determine what operations - /// a certificate can be used for. - /// A value of indicates that - /// there are no restrictions on the use of the - /// . - /// - [Flags] - public enum X509KeyUsageFlags { - /// - /// No limitations for the key usage are set. - /// - /// - /// The key may be used for anything. - /// - None = 0, - - /// - /// The key may only be used for enciphering data during key agreement. - /// - /// - /// When both the bit and the - /// bit are both set, the key - /// may be used only for enciphering data while - /// performing key agreement. - /// - EncipherOnly = 1 << 0, - - /// - /// The key may be used for verifying signatures on - /// certificate revocation lists (CRLs). - /// - CrlSign = 1 << 1, - - /// - /// The key may be used for verifying signatures on certificates. - /// - KeyCertSign = 1 << 2, - - /// - /// The key is meant to be used for key agreement. - /// - KeyAgreement = 1 << 3, - - /// - /// The key may be used for data encipherment. - /// - DataEncipherment = 1 << 4, - - /// - /// The key is meant to be used for key encipherment. - /// - KeyEncipherment = 1 << 5, - - /// - /// The key may be used to verify digital signatures used to - /// provide a non-repudiation service. - /// - NonRepudiation = 1 << 6, - - /// - /// The key may be used for digitally signing data. - /// - DigitalSignature = 1 << 7, - - /// - /// The key may only be used for deciphering data during key agreement. - /// - /// - /// When both the bit and the - /// bit are both set, the key - /// may be used only for deciphering data while - /// performing key agreement. - /// - DecipherOnly = 1 << 15 - } - - enum X509KeyUsageBits { - DigitalSignature, - NonRepudiation, - KeyEncipherment, - DataEncipherment, - KeyAgreement, - KeyCertSign, - CrlSign, - EncipherOnly, - DecipherOnly, - } -} diff --git a/src/MimeKit/DomainList.cs b/src/MimeKit/DomainList.cs deleted file mode 100644 index 7789607..0000000 --- a/src/MimeKit/DomainList.cs +++ /dev/null @@ -1,477 +0,0 @@ -// -// DomainList.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A domain list. - /// - /// - /// Represents a list of domains, such as those that an email was routed through. - /// - public class DomainList : IList - { - readonly static byte[] DomainSentinels = new [] { (byte) ',', (byte) ':' }; - readonly List domains; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new based on the domains provided. - /// - /// A domain list. - /// - /// is null. - /// - public DomainList (IEnumerable domains) - { - if (domains == null) - throw new ArgumentNullException (nameof (domains)); - - this.domains = new List (domains); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public DomainList () - { - domains = new List (); - } - - #region IList implementation - - /// - /// Gets the index of the requested domain, if it exists. - /// - /// - /// Finds the index of the specified domain, if it exists. - /// - /// The index of the requested domain; otherwise -1. - /// The domain. - /// - /// is null. - /// - public int IndexOf (string domain) - { - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - return domains.IndexOf (domain); - } - - /// - /// Insert the domain at the specified index. - /// - /// - /// Inserts the domain at the specified index in the list. - /// - /// The index to insert the domain. - /// The domain to insert. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void Insert (int index, string domain) - { - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - domains.Insert (index, domain); - OnChanged (); - } - - /// - /// Removes the domain at the specified index. - /// - /// - /// Removes the domain at the specified index. - /// - /// The index. - /// - /// is out of range. - /// - public void RemoveAt (int index) - { - domains.RemoveAt (index); - OnChanged (); - } - - /// - /// Gets or sets the domain at the specified index. - /// - /// - /// Gets or sets the domain at the specified index. - /// - /// The domain at the specified index. - /// The index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public string this [int index] { - get { return domains[index]; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (domains[index] == value) - return; - - domains[index] = value; - OnChanged (); - } - } - - #endregion - - #region ICollection implementation - - /// - /// Add the specified domain. - /// - /// - /// Adds the specified domain to the end of the list. - /// - /// The domain. - /// - /// is null. - /// - public void Add (string domain) - { - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - domains.Add (domain); - OnChanged (); - } - - /// - /// Clears the domain list. - /// - /// - /// Removes all of the domains in the list. - /// - public void Clear () - { - domains.Clear (); - OnChanged (); - } - - /// - /// Checks if the contains the specified domain. - /// - /// - /// Determines whether or not the domain list contains the specified domain. - /// - /// true if the specified domain is contained; - /// otherwise false. - /// The domain. - /// - /// is null. - /// - public bool Contains (string domain) - { - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - return domains.Contains (domain); - } - - /// - /// Copies all of the domains in the to the specified array. - /// - /// - /// Copies all of the domains within the into the array, - /// starting at the specified array index. - /// - /// The array to copy the domains to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (string[] array, int arrayIndex) - { - domains.CopyTo (array, arrayIndex); - } - - /// - /// Removes the specified domain. - /// - /// - /// Removes the first instance of the specified domain from the list if it exists. - /// - /// true if the domain was removed; otherwise false. - /// The domain. - /// - /// is null. - /// - public bool Remove (string domain) - { - if (domain == null) - throw new ArgumentNullException (nameof (domain)); - - if (domains.Remove (domain)) { - OnChanged (); - return true; - } - - return false; - } - - /// - /// Gets the number of domains in the . - /// - /// - /// Indicates the number of domains in the list. - /// - /// The number of domains. - public int Count { - get { return domains.Count; } - } - - /// - /// Get a value indicating whether the is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the list of domains. - /// - /// - /// Gets an enumerator for the list of domains. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return domains.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Gets an enumerator for the list of domains. - /// - /// - /// Gets an enumerator for the list of domains. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return domains.GetEnumerator (); - } - - #endregion - - static bool IsNullOrWhiteSpace (string value) - { - if (string.IsNullOrEmpty (value)) - return true; - - for (int i = 0; i < value.Length; i++) { - if (!char.IsWhiteSpace (value[i])) - return false; - } - - return true; - } - - internal string Encode (FormatOptions options) - { - var builder = new StringBuilder (); - - for (int i = 0; i < domains.Count; i++) { - if (IsNullOrWhiteSpace (domains[i])) - continue; - - if (builder.Length > 0) - builder.Append (','); - - builder.Append ('@'); - - if (!options.International && ParseUtils.IsInternational (domains[i])) { - var domain = ParseUtils.IdnEncode (domains[i]); - - builder.Append (domain); - } else { - builder.Append (domains[i]); - } - } - - return builder.ToString (); - } - - /// - /// Returns a string representation of the list of domains. - /// - /// - /// Each non-empty domain string will be prepended by an '@'. - /// If there are multiple domains in the list, they will be separated by a comma. - /// - /// A string representing the . - public override string ToString () - { - var builder = new StringBuilder (); - - for (int i = 0; i < domains.Count; i++) { - if (IsNullOrWhiteSpace (domains[i])) - continue; - - if (builder.Length > 0) - builder.Append (','); - - builder.Append ('@'); - - builder.Append (domains[i]); - } - - return builder.ToString (); - } - - internal event EventHandler Changed; - - void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - - /// - /// Try to parse a list of domains. - /// - /// - /// Attempts to parse a from the text buffer starting at the - /// specified index. The index will only be updated if a was - /// successfully parsed. - /// - /// true if a was successfully parsed; - /// false otherwise. - /// The buffer to parse. - /// The index to start parsing. - /// An index of the end of the input. - /// A flag indicating whether or not an - /// exception should be thrown on error. - /// The parsed DomainList. - internal static bool TryParse (byte[] buffer, ref int index, int endIndex, bool throwOnError, out DomainList route) - { - var domains = new List (); - int startIndex = index; - string domain; - - route = null; - - do { - // skip over the '@' - index++; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete domain-list at offset: {0}", startIndex), startIndex, index); - - return false; - } - - if (!ParseUtils.TryParseDomain (buffer, ref index, endIndex, DomainSentinels, throwOnError, out domain)) - return false; - - domains.Add (domain); - - // Note: obs-domain-list allows for null domains between commas - do { - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex || buffer[index] != (byte) ',') - break; - - index++; - } while (true); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, throwOnError)) - return false; - } while (index < buffer.Length && buffer[index] == (byte) '@'); - - route = new DomainList (domains); - - return true; - } - - /// - /// Try to parse a list of domains. - /// - /// - /// Attempts to parse a from the supplied text. The index - /// will only be updated if a was successfully parsed. - /// - /// true if a was successfully parsed; - /// false otherwise. - /// The text to parse. - /// The parsed DomainList. - /// - /// is null. - /// - public static bool TryParse (string text, out DomainList route) - { - int index = 0; - - if (text == null) - throw new ArgumentNullException (nameof (text)); - - var buffer = Encoding.UTF8.GetBytes (text); - - return TryParse (buffer, ref index, buffer.Length, false, out route); - } - } -} diff --git a/src/MimeKit/EncodingConstraint.cs b/src/MimeKit/EncodingConstraint.cs deleted file mode 100644 index d3ce03b..0000000 --- a/src/MimeKit/EncodingConstraint.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -// EncodingConstraint.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit { - /// - /// A content encoding constraint. - /// - /// - /// Not all message transports support binary or 8-bit data, so it becomes - /// necessary to constrain the content encoding to a subset of the possible - /// Content-Transfer-Encoding values. - /// - public enum EncodingConstraint { - /// - /// There are no encoding constraints, the content may contain any byte. - /// - None, - - /// - /// The content may contain bytes with the high bit set, but must not contain any zero-bytes. - /// - EightBit, - - /// - /// The content may only contain bytes within the 7-bit ASCII range. - /// - SevenBit, - } -} diff --git a/src/MimeKit/Encodings/Base64Decoder.cs b/src/MimeKit/Encodings/Base64Decoder.cs deleted file mode 100644 index ba2e5b2..0000000 --- a/src/MimeKit/Encodings/Base64Decoder.cs +++ /dev/null @@ -1,251 +0,0 @@ -// -// Base64Decoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Encodings { - /// - /// Incrementally decodes content encoded with the base64 encoding. - /// - /// - /// Base64 is an encoding often used in MIME to encode binary content such - /// as images and other types of multi-media to ensure that the data remains - /// intact when sent via 7bit transports such as SMTP. - /// - public class Base64Decoder : IMimeDecoder - { - static readonly byte[] base64_rank = new byte[256] { - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, - 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, - 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - }; - - uint saved; - byte bytes; - byte npad; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new base64 decoder. - /// - public Base64Decoder () - { - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current decoder. - /// - /// A new with identical state. - public IMimeDecoder Clone () - { - var decoder = new Base64Decoder (); - - decoder.saved = saved; - decoder.bytes = bytes; - decoder.npad = npad; - - return decoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the decoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.Base64; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to decode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - // may require up to 3 padding bytes - return inputLength + 3; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the decoded input.", nameof (output)); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// A pointer to the beginning of the input buffer. - /// The length of the input buffer. - /// A pointer to the beginning of the output buffer. - public unsafe int Decode (byte* input, int length, byte* output) - { - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - - // decode every quartet into a triplet - while (inptr < inend) { - byte c = base64_rank[*inptr++]; - if (c != 0xFF) { - saved = (saved << 6) | c; - bytes++; - - if (bytes == 4) { - *outptr++ = (byte) ((saved >> 16) & 0xFF); - *outptr++ = (byte) ((saved >> 8) & 0xFF); - *outptr++ = (byte) (saved & 0xFF); - saved = 0; - bytes = 0; - - if (npad > 0) { - outptr -= npad; - npad = 0; - } - } - } - } - - // Note: we can drop 1 output character per trailing '=' (up to 2) - for (int eq = 0; inptr > input && eq < 2; ) { - inptr--; - - if (base64_rank[*inptr] != 0xFF) { - if (*inptr == '=' && outptr > output) { - if (bytes == 0) { - // we've got a full quartet, so it's safe to drop an output character. - outptr--; - } else if (npad < 2) { - // keep a record of the # of '='s at the end of the input (up to 2) - npad++; - } - - eq++; - } else { - break; - } - } - } - - return (int) (outptr - output); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Decode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Decode (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the decoder. - /// - /// - /// Resets the state of the decoder. - /// - public void Reset () - { - saved = 0; - bytes = 0; - npad = 0; - } - } -} diff --git a/src/MimeKit/Encodings/Base64Encoder.cs b/src/MimeKit/Encodings/Base64Encoder.cs deleted file mode 100644 index 5dcf0f4..0000000 --- a/src/MimeKit/Encodings/Base64Encoder.cs +++ /dev/null @@ -1,337 +0,0 @@ -// -// Base64Encoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Encodings { - /// - /// Incrementally encodes content using the base64 encoding. - /// - /// - /// Base64 is an encoding often used in MIME to encode binary content such - /// as images and other types of multi-media to ensure that the data remains - /// intact when sent via 7bit transports such as SMTP. - /// - public class Base64Encoder : IMimeEncoder - { - static readonly byte[] base64_alphabet = { - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, - 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F - }; - - readonly int quartetsPerLine; - readonly bool rfc2047; - int quartets; - byte saved1; - byte saved2; - byte saved; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new base64 encoder. - /// - /// true if this encoder will be used to encode rfc2047 encoded-word payloads; false otherwise. - /// The maximum number of octets allowed per line (not counting the CRLF). Must be between 60 and 998 (inclusive). - /// - /// is not between 60 and 998 (inclusive). - /// - internal Base64Encoder (bool rfc2047, int maxLineLength = 72) - { - if (maxLineLength < FormatOptions.MinimumLineLength || maxLineLength > FormatOptions.MaximumLineLength) - throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - - quartetsPerLine = maxLineLength / 4; - this.rfc2047 = rfc2047; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new base64 encoder. - /// - /// The maximum number of octets allowed per line (not counting the CRLF). Must be between 60 and 998 (inclusive). - /// - /// is not between 60 and 998 (inclusive). - /// - public Base64Encoder (int maxLineLength = 72) : this (false, maxLineLength) - { - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current encoder. - /// - /// A new with identical state. - public IMimeEncoder Clone () - { - var encoder = new Base64Encoder (rfc2047, quartetsPerLine * 4); - - encoder.quartets = quartets; - encoder.saved1 = saved1; - encoder.saved2 = saved2; - encoder.saved = saved; - - return encoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.Base64; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to encode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - if (rfc2047) - return ((inputLength + 2) / 3) * 4; - - int maxLineLength = (quartetsPerLine * 4) + 1; - int maxInputPerLine = quartetsPerLine * 3; - - return (((inputLength + 2) / maxInputPerLine) * maxLineLength) + maxLineLength; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the encoded input.", nameof (output)); - } - - unsafe int Encode (byte* input, int length, byte* output) - { - if (length == 0) - return 0; - - int remaining = length; - byte* outptr = output; - byte* inptr = input; - - if (length + saved > 2) { - byte* inend = inptr + length - 2; - int c1, c2, c3; - - c1 = saved < 1 ? *inptr++ : saved1; - c2 = saved < 2 ? *inptr++ : saved2; - c3 = *inptr++; - - do { - // encode our triplet into a quartet - *outptr++ = base64_alphabet[c1 >> 2]; - *outptr++ = base64_alphabet[(c2 >> 4) | ((c1 & 0x3) << 4)]; - *outptr++ = base64_alphabet[((c2 & 0x0f) << 2) | (c3 >> 6)]; - *outptr++ = base64_alphabet[c3 & 0x3f]; - - // encode 18 quartets per line - if (!rfc2047 && (++quartets) >= quartetsPerLine) { - *outptr++ = (byte) '\n'; - quartets = 0; - } - - if (inptr >= inend) - break; - - c1 = *inptr++; - c2 = *inptr++; - c3 = *inptr++; - } while (true); - - remaining = 2 - (int) (inptr - inend); - saved = 0; - } - - if (remaining > 0) { - // At this point, saved can only be 0 or 1. - if (saved == 0) { - // We can have up to 2 remaining input bytes. - saved = (byte) remaining; - saved1 = *inptr++; - if (remaining == 2) - saved2 = *inptr; - else - saved2 = 0; - } else { - // We have 1 remaining input byte. - saved2 = *inptr++; - saved = 2; - } - } - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer. - /// - /// - /// Encodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Encode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Encode (inptr + startIndex, length, outptr); - } - } - } - - unsafe int Flush (byte* input, int length, byte* output) - { - byte* outptr = output; - - if (length > 0) - outptr += Encode (input, length, output); - - if (saved >= 1) { - int c1 = saved1; - int c2 = saved2; - - *outptr++ = base64_alphabet[c1 >> 2]; - *outptr++ = base64_alphabet[c2 >> 4 | ((c1 & 0x3) << 4)]; - if (saved == 2) - *outptr++ = base64_alphabet[(c2 & 0x0f) << 2]; - else - *outptr++ = (byte) '='; - *outptr++ = (byte) '='; - } - - if (!rfc2047) - *outptr++ = (byte) '\n'; - - Reset (); - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer, flushing any internal buffer state as well. - /// - /// - /// Encodes the specified input into the output buffer, flusing any internal state as well. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Flush (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Flush (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the encoder. - /// - /// - /// Resets the state of the encoder. - /// - public void Reset () - { - quartets = 0; - saved1 = 0; - saved2 = 0; - saved = 0; - } - } -} diff --git a/src/MimeKit/Encodings/HexDecoder.cs b/src/MimeKit/Encodings/HexDecoder.cs deleted file mode 100644 index d7fb2e5..0000000 --- a/src/MimeKit/Encodings/HexDecoder.cs +++ /dev/null @@ -1,232 +0,0 @@ -// -// HexDecoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; - -namespace MimeKit.Encodings { - /// - /// Incrementally decodes content encoded with a Uri hex encoding. - /// - /// - /// This is mostly meant for decoding parameter values encoded using - /// the rules specified by rfc2184 and rfc2231. - /// - public class HexDecoder : IMimeDecoder - { - enum HexDecoderState : byte { - PassThrough, - Percent, - DecodeByte - } - - HexDecoderState state; - byte saved; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new hex decoder. - /// - public HexDecoder () - { - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current decoder. - /// - /// A new with identical state. - public IMimeDecoder Clone () - { - var decoder = new HexDecoder (); - - decoder.state = state; - decoder.saved = saved; - - return decoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the decoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.Default; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to decode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - // add an extra 3 bytes for the saved input byte from previous decode step (in case it is invalid hex) - return inputLength + 3; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the decoded input.", nameof (output)); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// A pointer to the beginning of the input buffer. - /// The length of the input buffer. - /// A pointer to the beginning of the output buffer. - public unsafe int Decode (byte* input, int length, byte* output) - { - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - byte c; - - while (inptr < inend) { - switch (state) { - case HexDecoderState.PassThrough: - while (inptr < inend) { - c = *inptr++; - - if (c == '%') { - state = HexDecoderState.Percent; - break; - } - - *outptr++ = c; - } - break; - case HexDecoderState.Percent: - c = *inptr++; - state = HexDecoderState.DecodeByte; - saved = c; - break; - case HexDecoderState.DecodeByte: - c = *inptr++; - if (c.IsXDigit () && saved.IsXDigit ()) { - saved = saved.ToXDigit (); - c = c.ToXDigit (); - - *outptr++ = (byte) ((saved << 4) | c); - } else { - // invalid encoded sequence - pass it through undecoded - *outptr++ = (byte) '%'; - *outptr++ = saved; - *outptr++ = c; - } - - state = HexDecoderState.PassThrough; - break; - } - } - - return (int) (outptr - output); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Decode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Decode (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the decoder. - /// - /// - /// Resets the state of the decoder. - /// - public void Reset () - { - state = HexDecoderState.PassThrough; - saved = 0; - } - } -} diff --git a/src/MimeKit/Encodings/HexEncoder.cs b/src/MimeKit/Encodings/HexEncoder.cs deleted file mode 100644 index 5a7beae..0000000 --- a/src/MimeKit/Encodings/HexEncoder.cs +++ /dev/null @@ -1,216 +0,0 @@ -// -// HexEncoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; - -namespace MimeKit.Encodings { - /// - /// Incrementally encodes content using a Uri hex encoding. - /// - /// - /// This is mostly meant for decoding parameter values encoded using - /// the rules specified by rfc2184 and rfc2231. - /// - public class HexEncoder : IMimeEncoder - { - static readonly byte[] hex_alphabet = new byte[16] { - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // '0' -> '7' - 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, // '8' -> 'F' - }; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new hex encoder. - /// - public HexEncoder () - { - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current encoder. - /// - /// A new with identical state. - public IMimeEncoder Clone () - { - return new HexEncoder (); - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.Default; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to encode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - return inputLength * 3; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the encoded input.", nameof (output)); - } - - static unsafe int Encode (byte* input, int length, byte* output) - { - if (length == 0) - return 0; - - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - - while (inptr < inend) { - byte c = *inptr++; - - if (c.IsAttr ()) { - *outptr++ = c; - } else { - *outptr++ = (byte) '%'; - *outptr++ = hex_alphabet[(c >> 4) & 0x0f]; - *outptr++ = hex_alphabet[c & 0x0f]; - } - } - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer. - /// - /// - /// Encodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Encode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Encode (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Encodes the specified input into the output buffer, flushing any internal buffer state as well. - /// - /// - /// Encodes the specified input into the output buffer, flusing any internal state as well. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Flush (byte[] input, int startIndex, int length, byte[] output) - { - return Encode (input, startIndex, length, output); - } - - /// - /// Resets the encoder. - /// - /// - /// Resets the state of the encoder. - /// - public void Reset () - { - } - } -} diff --git a/src/MimeKit/Encodings/IMimeDecoder.cs b/src/MimeKit/Encodings/IMimeDecoder.cs deleted file mode 100644 index dec3d76..0000000 --- a/src/MimeKit/Encodings/IMimeDecoder.cs +++ /dev/null @@ -1,117 +0,0 @@ -// -// IMimeDecoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Encodings { - /// - /// An interface for incrementally decoding content. - /// - /// - /// An interface for incrementally decoding content. - /// - public interface IMimeDecoder - { - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the decoder supports. - /// - /// The encoding. - ContentEncoding Encoding { get; } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current decoder. - /// - /// A new with identical state. - IMimeDecoder Clone (); - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to decode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - int EstimateOutputLength (int inputLength); - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// A pointer to the beginning of the input buffer. - /// The length of the input buffer. - /// A pointer to the beginning of the output buffer. - unsafe int Decode (byte* input, int length, byte* output); - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - int Decode (byte[] input, int startIndex, int length, byte[] output); - - /// - /// Resets the decoder. - /// - /// - /// Resets the state of the decoder. - /// - void Reset (); - } -} diff --git a/src/MimeKit/Encodings/IMimeEncoder.cs b/src/MimeKit/Encodings/IMimeEncoder.cs deleted file mode 100644 index 741bb12..0000000 --- a/src/MimeKit/Encodings/IMimeEncoder.cs +++ /dev/null @@ -1,132 +0,0 @@ -// -// IMimeEncoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Encodings { - /// - /// An interface for incrementally encoding content. - /// - /// - /// An interface for incrementally encoding content. - /// - public interface IMimeEncoder - { - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - ContentEncoding Encoding { get; } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current encoder. - /// - /// A new with identical state. - IMimeEncoder Clone (); - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to encode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - int EstimateOutputLength (int inputLength); - - /// - /// Encodes the specified input into the output buffer. - /// - /// - /// Encodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - int Encode (byte[] input, int startIndex, int length, byte[] output); - - /// - /// Encodes the specified input into the output buffer, flushing any internal buffer state as well. - /// - /// - /// Encodes the specified input into the output buffer, flusing any internal state as well. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - int Flush (byte[] input, int startIndex, int length, byte[] output); - - /// - /// Resets the encoder. - /// - /// - /// Resets the state of the encoder. - /// - void Reset (); - } -} diff --git a/src/MimeKit/Encodings/PassThroughDecoder.cs b/src/MimeKit/Encodings/PassThroughDecoder.cs deleted file mode 100644 index 87aaf73..0000000 --- a/src/MimeKit/Encodings/PassThroughDecoder.cs +++ /dev/null @@ -1,172 +0,0 @@ -// -// PassThroughDecoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Encodings { - /// - /// A pass-through decoder implementing the interface. - /// - /// - /// Simply copies data as-is from the input buffer into the output buffer. - /// - public class PassThroughDecoder : IMimeDecoder - { - /// - /// Initialize a new instance of the class. - /// - /// The encoding to return in the property. - /// - /// Creates a new pass-through decoder. - /// - public PassThroughDecoder (ContentEncoding encoding) - { - Encoding = encoding; - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current decoder. - /// - /// A new with identical state. - public IMimeDecoder Clone () - { - return new PassThroughDecoder (Encoding); - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the decoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get; private set; - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to decode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - return inputLength; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the decoded input.", nameof (output)); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Copies the input buffer into the output buffer, verbatim. - /// - /// The number of bytes written to the output buffer. - /// A pointer to the beginning of the input buffer. - /// The length of the input buffer. - /// A pointer to the beginning of the output buffer. - public unsafe int Decode (byte* input, int length, byte* output) - { - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - - while (inptr < inend) - *outptr++ = *inptr++; - - return length; - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Copies the input buffer into the output buffer, verbatim. - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Decode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Decode (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the decoder. - /// - /// - /// Resets the state of the decoder. - /// - public void Reset () - { - } - } -} diff --git a/src/MimeKit/Encodings/PassThroughEncoder.cs b/src/MimeKit/Encodings/PassThroughEncoder.cs deleted file mode 100644 index 3dc50db..0000000 --- a/src/MimeKit/Encodings/PassThroughEncoder.cs +++ /dev/null @@ -1,178 +0,0 @@ -// -// PassThroughEncoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Encodings { - /// - /// A pass-through encoder implementing the interface. - /// - /// - /// Simply copies data as-is from the input buffer into the output buffer. - /// - public class PassThroughEncoder : IMimeEncoder - { - /// - /// Initialize a new instance of the class. - /// - /// The encoding to return in the property. - /// - /// Creates a new pass-through encoder. - /// - public PassThroughEncoder (ContentEncoding encoding) - { - Encoding = encoding; - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current encoder. - /// - /// A new with identical state. - public IMimeEncoder Clone () - { - return new PassThroughEncoder (Encoding); - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get; private set; - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to encode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - return inputLength; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the encoded input.", nameof (output)); - } - - /// - /// Encodes the specified input into the output buffer. - /// - /// - /// Copies the input buffer into the output buffer, verbatim. - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Encode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - Buffer.BlockCopy (input, startIndex, output, 0, length); - - return length; - } - - /// - /// Encodes the specified input into the output buffer, flushing any internal buffer state as well. - /// - /// - /// Copies the input buffer into the output buffer, verbatim. - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Flush (byte[] input, int startIndex, int length, byte[] output) - { - return Encode (input, startIndex, length, output); - } - - /// - /// Resets the encoder. - /// - /// - /// Resets the state of the encoder. - /// - public void Reset () - { - } - } -} diff --git a/src/MimeKit/Encodings/QEncoder.cs b/src/MimeKit/Encodings/QEncoder.cs deleted file mode 100644 index e8a5ba3..0000000 --- a/src/MimeKit/Encodings/QEncoder.cs +++ /dev/null @@ -1,260 +0,0 @@ -// -// QEncoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; - -namespace MimeKit.Encodings { - /// - /// Q-Encoding mode. - /// - /// - /// The encoding mode for the 'Q' encoding used in rfc2047. - /// - public enum QEncodeMode : byte { - /// - /// A mode for encoding phrases, as defined by rfc822. - /// - Phrase, - - /// - /// A mode for encoding text. - /// - Text - } - - /// - /// Incrementally encodes content using a variation of the quoted-printable encoding - /// that is specifically meant to be used for rfc2047 encoded-word tokens. - /// - /// - /// The Q-Encoding is an encoding often used in MIME to encode textual content outside - /// of the ASCII range within an rfc2047 encoded-word token in order to ensure that - /// the text remains intact when sent via 7bit transports such as SMTP. - /// - public class QEncoder : IMimeEncoder - { - static readonly byte[] hex_alphabet = new byte[16] { - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // '0' -> '7' - 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, // '8' -> 'F' - }; - - readonly CharType mask; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new rfc2047 quoted-printable encoder. - /// - /// The rfc2047 encoding mode. - public QEncoder (QEncodeMode mode) - { - mask = mode == QEncodeMode.Phrase ? CharType.IsEncodedPhraseSafe : CharType.IsEncodedWordSafe; - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current encoder. - /// - /// A new with identical state. - public IMimeEncoder Clone () - { - return new QEncoder (mask == CharType.IsEncodedPhraseSafe ? QEncodeMode.Phrase : QEncodeMode.Text); - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.QuotedPrintable; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to encode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - return inputLength * 3; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the encoded input.", nameof (output)); - } - - unsafe int Encode (byte* input, int length, byte* output) - { - if (length == 0) - return 0; - - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - - while (inptr < inend) { - byte c = *inptr++; - - if (c == ' ') { - *outptr++ = (byte) '_'; - } else if (c != '_' && c.IsType (mask)) { - *outptr++ = c; - } else { - *outptr++ = (byte) '='; - *outptr++ = hex_alphabet[(c >> 4) & 0x0f]; - *outptr++ = hex_alphabet[c & 0x0f]; - } - } - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer. - /// - /// - /// Encodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Encode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Encode (inptr + startIndex, length, outptr); - } - } - } - - unsafe int Flush (byte* input, int length, byte* output) - { - byte* outptr = output; - - if (length > 0) - outptr += Encode (input, length, output); - - Reset (); - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer, flushing any internal buffer state as well. - /// - /// - /// Encodes the specified input into the output buffer, flusing any internal state as well. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Flush (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Flush (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the encoder. - /// - /// - /// Resets the state of the encoder. - /// - public void Reset () - { - } - } -} diff --git a/src/MimeKit/Encodings/QuotedPrintableDecoder.cs b/src/MimeKit/Encodings/QuotedPrintableDecoder.cs deleted file mode 100644 index eee3f16..0000000 --- a/src/MimeKit/Encodings/QuotedPrintableDecoder.cs +++ /dev/null @@ -1,277 +0,0 @@ -// -// QuotedPrintableDecoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; - -namespace MimeKit.Encodings { - /// - /// Incrementally decodes content encoded with the quoted-printable encoding. - /// - /// - /// Quoted-Printable is an encoding often used in MIME to textual content outside - /// of the ASCII range in order to ensure that the text remains intact when sent - /// via 7bit transports such as SMTP. - /// - public class QuotedPrintableDecoder : IMimeDecoder - { - enum QpDecoderState : byte { - PassThrough, - EqualSign, - SoftBreak, - DecodeByte - } - - readonly bool rfc2047; - QpDecoderState state; - byte saved; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new quoted-printable decoder. - /// - /// - /// true if this decoder will be used to decode rfc2047 encoded-word payloads; false otherwise. - /// - public QuotedPrintableDecoder (bool rfc2047) - { - this.rfc2047 = rfc2047; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new quoted-printable decoder. - /// - public QuotedPrintableDecoder () : this (false) - { - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current decoder. - /// - /// A new with identical state. - public IMimeDecoder Clone () - { - var decoder = new QuotedPrintableDecoder (rfc2047); - - decoder.state = state; - decoder.saved = saved; - - return decoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the decoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.QuotedPrintable; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to decode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - // add an extra 3 bytes for the saved input byte from previous decode step (in case it is invalid hex) - return inputLength + 3; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the decoded input.", nameof (output)); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// A pointer to the beginning of the input buffer. - /// The length of the input buffer. - /// A pointer to the beginning of the output buffer. - public unsafe int Decode (byte* input, int length, byte* output) - { - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - byte c; - - while (inptr < inend) { - switch (state) { - case QpDecoderState.PassThrough: - while (inptr < inend) { - c = *inptr++; - - if (c == '=') { - state = QpDecoderState.EqualSign; - break; - } else if (rfc2047 && c == '_') { - *outptr++ = (byte) ' '; - } else { - *outptr++ = c; - } - } - break; - case QpDecoderState.EqualSign: - c = *inptr++; - - if (c.IsXDigit ()) { - state = QpDecoderState.DecodeByte; - saved = c; - } else if (c == '=') { - // invalid encoded sequence - pass it through undecoded - *outptr++ = (byte) '='; - } else if (c == '\r') { - state = QpDecoderState.SoftBreak; - } else if (c == '\n') { - state = QpDecoderState.PassThrough; - } else { - // invalid encoded sequence - pass it through undecoded - state = QpDecoderState.PassThrough; - *outptr++ = (byte) '='; - *outptr++ = c; - } - break; - case QpDecoderState.SoftBreak: - state = QpDecoderState.PassThrough; - c = *inptr++; - - if (c != '\n') { - // invalid encoded sequence - pass it through undecoded - *outptr++ = (byte) '='; - *outptr++ = (byte) '\r'; - *outptr++ = c; - } - break; - case QpDecoderState.DecodeByte: - c = *inptr++; - if (c.IsXDigit ()) { - saved = saved.ToXDigit (); - c = c.ToXDigit (); - - *outptr++ = (byte) ((saved << 4) | c); - } else { - // invalid encoded sequence - pass it through undecoded - *outptr++ = (byte) '='; - *outptr++ = saved; - *outptr++ = c; - } - - state = QpDecoderState.PassThrough; - break; - } - } - - return (int) (outptr - output); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Decode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Decode (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the decoder. - /// - /// - /// Resets the state of the decoder. - /// - public void Reset () - { - state = QpDecoderState.PassThrough; - saved = 0; - } - } -} diff --git a/src/MimeKit/Encodings/QuotedPrintableEncoder.cs b/src/MimeKit/Encodings/QuotedPrintableEncoder.cs deleted file mode 100644 index ff7d4fc..0000000 --- a/src/MimeKit/Encodings/QuotedPrintableEncoder.cs +++ /dev/null @@ -1,317 +0,0 @@ -// -// QuotedPrintableEncoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; - -namespace MimeKit.Encodings { - /// - /// Incrementally encodes content using the quoted-printable encoding. - /// - /// - /// Quoted-Printable is an encoding often used in MIME to encode textual content - /// outside of the ASCII range in order to ensure that the text remains intact - /// when sent via 7bit transports such as SMTP. - /// - public class QuotedPrintableEncoder : IMimeEncoder - { - static readonly byte[] hex_alphabet = new byte[16] { - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // '0' -> '7' - 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, // '8' -> 'F' - }; - - const int TripletsPerLine = 23; - const int DesiredLineLength = TripletsPerLine * 3; - const int MaxLineLength = DesiredLineLength + 2; // "=\n" - - short currentLineLength; - short saved; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new quoted-printable encoder. - /// - public QuotedPrintableEncoder () - { - Reset (); - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current encoder. - /// - /// A new with identical state. - public IMimeEncoder Clone () - { - var encoder = new QuotedPrintableEncoder (); - - encoder.currentLineLength = currentLineLength; - encoder.saved = saved; - - return encoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.QuotedPrintable; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to encode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - return ((inputLength / TripletsPerLine) * MaxLineLength) + MaxLineLength; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the encoded input.", nameof (output)); - } - - unsafe int Encode (byte* input, int length, byte* output) - { - if (length == 0) - return 0; - - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - - while (inptr < inend) { - byte c = *inptr++; - - if (c == (byte) '\r') { - if (saved != -1) { - *outptr++ = (byte) '='; - *outptr++ = hex_alphabet[(saved >> 4) & 0x0f]; - *outptr++ = hex_alphabet[saved & 0x0f]; - currentLineLength += 3; - } - - saved = c; - } else if (c == (byte) '\n') { - if (saved != -1 && saved != '\r') { - *outptr++ = (byte) '='; - *outptr++ = hex_alphabet[(saved >> 4) & 0x0f]; - *outptr++ = hex_alphabet[saved & 0x0f]; - } - - *outptr++ = (byte) '\n'; - currentLineLength = 0; - saved = -1; - } else { - if (saved != -1) { - byte b = (byte) saved; - - if (b.IsQpSafe ()) { - *outptr++ = b; - currentLineLength++; - } else { - *outptr++ = (byte) '='; - *outptr++ = hex_alphabet[(saved >> 4) & 0x0f]; - *outptr++ = hex_alphabet[saved & 0x0f]; - } - } - - if (currentLineLength > DesiredLineLength) { - *outptr++ = (byte) '='; - *outptr++ = (byte) '\n'; - currentLineLength = 0; - } - - if (c.IsQpSafe ()) { - // delay output of whitespace character - if (c.IsBlank ()) { - saved = c; - } else { - *outptr++ = c; - currentLineLength++; - saved = -1; - } - } else { - *outptr++ = (byte) '='; - *outptr++ = hex_alphabet[(c >> 4) & 0x0f]; - *outptr++ = hex_alphabet[c & 0x0f]; - currentLineLength += 3; - saved = -1; - } - } - } - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer. - /// - /// - /// Encodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Encode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Encode (inptr + startIndex, length, outptr); - } - } - } - - unsafe int Flush (byte* input, int length, byte* output) - { - byte* outptr = output; - - if (length > 0) - outptr += Encode (input, length, output); - - if (saved != -1) { - // spaces and tabs must be encoded if they the last character on the line - byte c = (byte) saved; - - if (c.IsBlank () || !c.IsQpSafe ()) { - *outptr++ = (byte) '='; - *outptr++ = hex_alphabet[(saved >> 4) & 0xf]; - *outptr++ = hex_alphabet[saved & 0xf]; - } else { - *outptr++ = c; - } - - // we end with =\n so that the \n isn't interpreted as - // a real \n when it gets decoded later - *outptr++ = (byte) '='; - *outptr++ = (byte) '\n'; - } - - Reset (); - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer, flushing any internal buffer state as well. - /// - /// - /// Encodes the specified input into the output buffer, flusing any internal state as well. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Flush (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Flush (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the encoder. - /// - /// - /// Resets the state of the encoder. - /// - public void Reset () - { - currentLineLength = 0; - saved = -1; - } - } -} diff --git a/src/MimeKit/Encodings/UUDecoder.cs b/src/MimeKit/Encodings/UUDecoder.cs deleted file mode 100644 index 59a86c8..0000000 --- a/src/MimeKit/Encodings/UUDecoder.cs +++ /dev/null @@ -1,418 +0,0 @@ -// -// UUDecoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Encodings { - /// - /// Incrementally decodes content encoded with the Unix-to-Unix encoding. - /// - /// - /// The UUEncoding is an encoding that predates MIME and was used to encode - /// binary content such as images and other types of multi-media to ensure - /// that the data remained intact when sent via 7bit transports such as SMTP. - /// These days, the UUEncoding has largely been deprecated in favour of - /// the base64 encoding, however, some older mail clients still use it. - /// - public class UUDecoder : IMimeDecoder - { - static readonly byte[] uudecode_rank = new byte[256] { - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - }; - - enum UUDecoderState : byte { - ExpectBegin, - B, - Be, - Beg, - Begi, - Begin, - ExpectPayload, - Payload, - Ended, - } - - readonly UUDecoderState initial; - UUDecoderState state; - byte nsaved; - byte uulen; - uint saved; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new Unix-to-Unix decoder. - /// - /// - /// If true, decoding begins immediately rather than after finding a begin-line. - /// - public UUDecoder (bool payloadOnly) - { - initial = payloadOnly ? UUDecoderState.Payload : UUDecoderState.ExpectBegin; - Reset (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new Unix-to-Unix decoder. - /// - public UUDecoder () : this (false) - { - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current decoder. - /// - /// A new with identical state. - public IMimeDecoder Clone () - { - var decoder = new UUDecoder (initial == UUDecoderState.Payload); - - decoder.state = state; - decoder.nsaved = nsaved; - decoder.saved = saved; - decoder.uulen = uulen; - - return decoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the decoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.UUEncode; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to decode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - // add an extra 3 bytes for the saved input bytes from previous decode step - return inputLength + 3; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the decoded input.", nameof (output)); - } - - unsafe byte* ScanBeginMarker (byte* inptr, byte* inend) - { - while (inptr < inend) { - if (state == UUDecoderState.ExpectBegin) { - if (nsaved != 0 && nsaved != (byte) '\n') { - while (inptr < inend && *inptr != (byte) '\n') - inptr++; - - if (inptr == inend) { - nsaved = *(inptr - 1); - return inptr; - } - - nsaved = *inptr++; - if (inptr == inend) - return inptr; - } - - nsaved = *inptr++; - if (nsaved != (byte) 'b') - continue; - - state = UUDecoderState.B; - if (inptr == inend) - return inptr; - } - - if (state == UUDecoderState.B) { - nsaved = *inptr++; - if (nsaved != (byte) 'e') { - state = UUDecoderState.ExpectBegin; - continue; - } - - state = UUDecoderState.Be; - if (inptr == inend) - return inptr; - } - - if (state == UUDecoderState.Be) { - nsaved = *inptr++; - if (nsaved != (byte) 'g') { - state = UUDecoderState.ExpectBegin; - continue; - } - - state = UUDecoderState.Beg; - if (inptr == inend) - return inptr; - } - - if (state == UUDecoderState.Beg) { - nsaved = *inptr++; - if (nsaved != (byte) 'i') { - state = UUDecoderState.ExpectBegin; - continue; - } - - state = UUDecoderState.Begi; - if (inptr == inend) - return inptr; - } - - if (state == UUDecoderState.Begi) { - nsaved = *inptr++; - if (nsaved != (byte) 'n') { - state = UUDecoderState.ExpectBegin; - continue; - } - - state = UUDecoderState.Begin; - if (inptr == inend) - return inptr; - } - - if (state == UUDecoderState.Begin) { - nsaved = *inptr++; - if (nsaved != (byte) ' ') { - state = UUDecoderState.ExpectBegin; - continue; - } - - state = UUDecoderState.ExpectPayload; - if (inptr == inend) - return inptr; - } - - if (state == UUDecoderState.ExpectPayload) { - while (inptr < inend && *inptr != (byte) '\n') - inptr++; - - if (inptr == inend) - return inptr; - - state = UUDecoderState.Payload; - nsaved = 0; - - return inptr + 1; - } - } - - return inptr; - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// A pointer to the beginning of the input buffer. - /// The length of the input buffer. - /// A pointer to the beginning of the output buffer. - public unsafe int Decode (byte* input, int length, byte* output) - { - if (state == UUDecoderState.Ended) - return 0; - - bool last_was_eoln = uulen == 0; - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - byte c; - - if (state < UUDecoderState.Payload) { - if ((inptr = ScanBeginMarker (inptr, inend)) == inend) - return 0; - } - - while (inptr < inend) { - if (*inptr == (byte) '\r') { - inptr++; - continue; - } - - if (*inptr == (byte) '\n') { - last_was_eoln = true; - inptr++; - continue; - } - - if (uulen == 0 || last_was_eoln) { - // first octet on a line is the uulen octet - uulen = uudecode_rank[*inptr]; - last_was_eoln = false; - if (uulen == 0) { - state = UUDecoderState.Ended; - break; - } - - inptr++; - continue; - } - - c = *inptr++; - - if (uulen > 0) { - // save the byte - saved = (saved << 8) | c; - nsaved++; - - if (nsaved == 4) { - byte b0 = (byte) ((saved >> 24) & 0xFF); - byte b1 = (byte) ((saved >> 16) & 0xFF); - byte b2 = (byte) ((saved >> 8) & 0xFF); - byte b3 = (byte) (saved & 0xFF); - - if (uulen >= 3) { - *outptr++ = (byte) (uudecode_rank[b0] << 2 | uudecode_rank[b1] >> 4); - *outptr++ = (byte) (uudecode_rank[b1] << 4 | uudecode_rank[b2] >> 2); - *outptr++ = (byte) (uudecode_rank[b2] << 6 | uudecode_rank[b3]); - uulen -= 3; - } else { - if (uulen >= 1) { - *outptr++ = (byte) (uudecode_rank[b0] << 2 | uudecode_rank[b1] >> 4); - uulen--; - } - - if (uulen >= 1) { - *outptr++ = (byte) (uudecode_rank[b1] << 4 | uudecode_rank[b2] >> 2); - uulen--; - } - } - - nsaved = 0; - saved = 0; - } - } else { - break; - } - } - - return (int) (outptr - output); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Decode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Decode (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the decoder. - /// - /// - /// Resets the state of the decoder. - /// - public void Reset () - { - state = initial; - nsaved = 0; - saved = 0; - uulen = 0; - } - } -} diff --git a/src/MimeKit/Encodings/UUEncoder.cs b/src/MimeKit/Encodings/UUEncoder.cs deleted file mode 100644 index eef18cd..0000000 --- a/src/MimeKit/Encodings/UUEncoder.cs +++ /dev/null @@ -1,375 +0,0 @@ -// -// UUEncoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Encodings { - /// - /// Incrementally encodes content using the Unix-to-Unix encoding. - /// - /// - /// The UUEncoding is an encoding that predates MIME and was used to encode - /// binary content such as images and other types of multi-media to ensure - /// that the data remained intact when sent via 7bit transports such as SMTP. - /// These days, the UUEncoding has largely been deprecated in favour of - /// the base64 encoding, however, some older mail clients still use it. - /// - public class UUEncoder : IMimeEncoder - { - const int MaxInputPerLine = 45; - const int MaxOutputPerLine = ((MaxInputPerLine / 3) * 4) + 2; - - readonly byte[] uubuf = new byte[60]; - uint saved; - byte nsaved; - byte uulen; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new Unix-to-Unix encoder. - /// - public UUEncoder () - { - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current encoder. - /// - /// A new with identical state. - public IMimeEncoder Clone () - { - var encoder = new UUEncoder (); - - Buffer.BlockCopy (uubuf, 0, encoder.uubuf, 0, uubuf.Length); - encoder.nsaved = nsaved; - encoder.saved = saved; - encoder.uulen = uulen; - - return encoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.UUEncode; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to encode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - return (((inputLength + 2) / MaxInputPerLine) * MaxOutputPerLine) + MaxOutputPerLine + 2; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the encoded input.", nameof (output)); - } - - static byte Encode (int c) - { - return c != 0 ? (byte) (c + 0x20) : (byte) '`'; - } - - unsafe int Encode (byte* input, int length, byte[] outbuf, byte* output, byte *uuptr) - { - if (length == 0) - return 0; - - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - byte* bufptr; - byte b0, b1, b2; - - if ((length + nsaved + uulen) < 45) { - // not enough input to write a full uuencoded line - bufptr = uuptr + ((uulen / 3) * 4); - } else { - bufptr = outptr + 1; - - if (uulen > 0) { - // copy the previous call's uubuf to output - int n = (uulen / 3) * 4; - - Buffer.BlockCopy (uubuf, 0, outbuf, 1, n); - bufptr += n; - } - } - - if (nsaved == 2) { - b0 = (byte) ((saved >> 8) & 0xFF); - b1 = (byte) (saved & 0xFF); - b2 = *inptr++; - nsaved = 0; - saved = 0; - - // convert 3 input bytes into 4 uuencoded bytes - *bufptr++ = Encode ((b0 >> 2) & 0x3F); - *bufptr++ = Encode (((b0 << 4) | ((b1 >> 4) & 0x0F)) & 0x3F); - *bufptr++ = Encode (((b1 << 2) | ((b2 >> 6) & 0x03)) & 0x3F); - *bufptr++ = Encode (b2 & 0x3F); - - uulen += 3; - } else if (nsaved == 1) { - if ((inptr + 2) < inend) { - b0 = (byte) (saved & 0xFF); - b1 = *inptr++; - b2 = *inptr++; - nsaved = 0; - saved = 0; - - // convert 3 input bytes into 4 uuencoded bytes - *bufptr++ = Encode ((b0 >> 2) & 0x3F); - *bufptr++ = Encode (((b0 << 4) | ((b1 >> 4) & 0x0F)) & 0x3F); - *bufptr++ = Encode (((b1 << 2) | ((b2 >> 6) & 0x03)) & 0x3F); - *bufptr++ = Encode (b2 & 0x3F); - - uulen += 3; - } else { - while (inptr < inend) { - saved = (saved << 8) | *inptr++; - nsaved++; - } - } - } - - do { - while (uulen < 45 && (inptr + 2) < inend) { - b0 = *inptr++; - b1 = *inptr++; - b2 = *inptr++; - - // convert 3 input bytes into 4 uuencoded bytes - *bufptr++ = Encode ((b0 >> 2) & 0x3F); - *bufptr++ = Encode (((b0 << 4) | ((b1 >> 4) & 0x0F)) & 0x3F); - *bufptr++ = Encode (((b1 << 2) | ((b2 >> 6) & 0x03)) & 0x3F); - *bufptr++ = Encode (b2 & 0x3F); - - uulen += 3; - } - - if (uulen >= 45) { - // output the uu line length - *outptr = Encode (uulen); - outptr += ((uulen / 3) * 4) + 1; - *outptr++ = (byte) '\n'; - uulen = 0; - - if ((inptr + 45) <= inend) { - // we have enough input to output another full line - bufptr = outptr + 1; - } else { - bufptr = uuptr; - } - } else { - // not enough input to continue... - while (inptr < inend) { - saved = (saved << 8) | *inptr++; - nsaved++; - } - } - } while (inptr < inend); - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer. - /// - /// - /// Encodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Encode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output, uuptr = uubuf) { - return Encode (inptr + startIndex, length, output, outptr, uuptr); - } - } - } - - unsafe int Flush (byte* input, int length, byte[] outbuf, byte* output, byte* uuptr) - { - byte* outptr = output; - - if (length > 0) - outptr += Encode (input, length, outbuf, output, uuptr); - - byte* bufptr = uuptr + ((uulen / 3) * 4); - byte uufill = 0; - - if (nsaved > 0) { - while (nsaved < 3) { - saved <<= 8; - uufill++; - nsaved++; - } - - if (nsaved == 3) { - // convert 3 input bytes into 4 uuencoded bytes - byte b0, b1, b2; - - b0 = (byte) ((saved >> 16) & 0xFF); - b1 = (byte) ((saved >> 8) & 0xFF); - b2 = (byte) (saved & 0xFF); - - *bufptr++ = Encode ((b0 >> 2) & 0x3F); - *bufptr++ = Encode (((b0 << 4) | ((b1 >> 4) & 0x0F)) & 0x3F); - *bufptr++ = Encode (((b1 << 2) | ((b2 >> 6) & 0x03)) & 0x3F); - *bufptr++ = Encode (b2 & 0x3F); - - uulen += 3; - nsaved = 0; - saved = 0; - } - } - - if (uulen > 0) { - int n = (uulen / 3) * 4; - - *outptr++ = Encode ((uulen - uufill) & 0xFF); - Buffer.BlockCopy (uubuf, 0, outbuf, (int) (outptr - output), n); - outptr += n; - - *outptr++ = (byte) '\n'; - uulen = 0; - } - - *outptr++ = Encode (uulen & 0xFF); - *outptr++ = (byte) '\n'; - - Reset (); - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer, flushing any internal buffer state as well. - /// - /// - /// Encodes the specified input into the output buffer, flusing any internal state as well. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Flush (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output, uuptr = uubuf) { - return Flush (inptr + startIndex, length, output, outptr, uuptr); - } - } - } - - /// - /// Resets the encoder. - /// - /// - /// Resets the state of the encoder. - /// - public void Reset () - { - nsaved = 0; - saved = 0; - uulen = 0; - } - } -} diff --git a/src/MimeKit/Encodings/YDecoder.cs b/src/MimeKit/Encodings/YDecoder.cs deleted file mode 100644 index 61d8cbb..0000000 --- a/src/MimeKit/Encodings/YDecoder.cs +++ /dev/null @@ -1,544 +0,0 @@ -// -// YDecoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; - -namespace MimeKit.Encodings { - /// - /// Incrementally decodes content encoded with the yEnc encoding. - /// - /// - /// The yEncoding is an encoding that is most commonly used with Usenet and - /// is a binary encoding that includes a 32-bit cyclic redundancy check. - /// For more information, see www.yenc.org. - /// - public class YDecoder : IMimeDecoder - { - enum YDecoderState : byte { - ExpectYBegin, - YBeginEqual, - YBeginEqualY, - YBeginEqualYB, - YBeginEqualYBe, - YBeginEqualYBeg, - YBeginEqualYBegi, - YBeginEqualYBegin, - ExpectYBeginNewLine, - - ExpectYPartOrPayload, - - YPartEqual, - YPartEqualY, - YPartEqualYP, - YPartEqualYPa, - YPartEqualYPar, - YPartEqualYPart, - ExpectYPartNewLine, - - Payload, - Ended, - } - - readonly YDecoderState initial; - YDecoderState state; - bool escaped; - byte octet; - bool eoln; - Crc32 crc; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new yEnc decoder. - /// - /// - /// If true, decoding begins immediately rather than after finding an =ybegin line. - /// - public YDecoder (bool payloadOnly) - { - initial = payloadOnly ? YDecoderState.Payload : YDecoderState.ExpectYBegin; - crc = new Crc32 (-1); - Reset (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new yEnc decoder. - /// - public YDecoder () : this (false) - { - } - - /// - /// Gets the checksum. - /// - /// - /// Gets the checksum. - /// - /// The checksum. - public int Checksum { - get { return crc.Checksum; } - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current decoder. - /// - /// A new with identical state. - public IMimeDecoder Clone () - { - var decoder = new YDecoder (initial == YDecoderState.Payload); - - decoder.crc = crc.Clone (); - decoder.escaped = escaped; - decoder.state = state; - decoder.octet = octet; - decoder.eoln = eoln; - - return decoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the decoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.Default; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to decode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - return inputLength; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the decoded input.", nameof (output)); - } - - unsafe byte* ScanYBeginMarker (byte* inptr, byte* inend) - { - while (inptr < inend) { - if (state == YDecoderState.ExpectYBegin) { - if (octet != (byte) '\n') { - while (inptr < inend && *inptr != (byte) '\n') - inptr++; - - if (inptr == inend) { - octet = *(inptr - 1); - break; - } - - octet = *inptr++; - if (inptr == inend) - break; - } - - octet = *inptr++; - if (octet != (byte) '=') - continue; - - state = YDecoderState.YBeginEqual; - if (inptr == inend) - break; - } - - if (state == YDecoderState.YBeginEqual) { - octet = *inptr++; - if (octet != (byte) 'y') { - state = YDecoderState.ExpectYBegin; - continue; - } - - state = YDecoderState.YBeginEqualY; - if (inptr == inend) - break; - } - - if (state == YDecoderState.YBeginEqualY) { - octet = *inptr++; - if (octet != (byte) 'b') { - state = YDecoderState.ExpectYBegin; - continue; - } - - state = YDecoderState.YBeginEqualYB; - if (inptr == inend) - break; - } - - if (state == YDecoderState.YBeginEqualYB) { - octet = *inptr++; - if (octet != (byte) 'e') { - state = YDecoderState.ExpectYBegin; - continue; - } - - state = YDecoderState.YBeginEqualYBe; - if (inptr == inend) - break; - } - - if (state == YDecoderState.YBeginEqualYBe) { - octet = *inptr++; - if (octet != (byte) 'g') { - state = YDecoderState.ExpectYBegin; - continue; - } - - state = YDecoderState.YBeginEqualYBeg; - if (inptr == inend) - break; - } - - if (state == YDecoderState.YBeginEqualYBeg) { - octet = *inptr++; - if (octet != (byte) 'i') { - state = YDecoderState.ExpectYBegin; - continue; - } - - state = YDecoderState.YBeginEqualYBegi; - if (inptr == inend) - break; - } - - if (state == YDecoderState.YBeginEqualYBegi) { - octet = *inptr++; - if (octet != (byte) 'n') { - state = YDecoderState.ExpectYBegin; - continue; - } - - state = YDecoderState.YBeginEqualYBegin; - if (inptr == inend) - break; - } - - if (state == YDecoderState.YBeginEqualYBegin) { - octet = *inptr++; - if (octet != (byte) ' ') { - state = YDecoderState.ExpectYBegin; - continue; - } - - state = YDecoderState.ExpectYBeginNewLine; - if (inptr == inend) - break; - } - - if (state == YDecoderState.ExpectYBeginNewLine) { - while (inptr < inend && *inptr != (byte) '\n') - inptr++; - - if (inptr == inend) { - octet = *(inptr - 1); - break; - } - - state = YDecoderState.ExpectYPartOrPayload; - octet = *inptr++; - - if (inptr == inend) - break; - } - - if (state == YDecoderState.ExpectYPartOrPayload) { - if (*inptr != (byte) '=') { - state = YDecoderState.Payload; - escaped = false; - eoln = true; - break; - } - - state = YDecoderState.YPartEqual; - octet = *inptr++; - escaped = true; - - if (inptr == inend) - break; - } - - if (state == YDecoderState.YPartEqual) { - if (*inptr != (byte) 'y') { - state = YDecoderState.Payload; - escaped = false; - eoln = true; - return inptr; - } - - state = YDecoderState.YPartEqualY; - octet = *inptr++; - - if (inptr == inend) - break; - } - - if (state == YDecoderState.YPartEqualY) { - if (*inptr == (byte) 'e') { - // we got an "=ye" which can only be an "=yend" - state = YDecoderState.Ended; - return inptr; - } - - if (*inptr != (byte) 'p') { - state = YDecoderState.ExpectYBeginNewLine; - continue; - } - - state = YDecoderState.YPartEqualYP; - octet = *inptr++; - - if (inptr == inend) - break; - } - - if (state == YDecoderState.YPartEqualYP) { - if (*inptr != (byte) 'a') { - state = YDecoderState.ExpectYBeginNewLine; - continue; - } - - state = YDecoderState.YPartEqualYPa; - octet = *inptr++; - - if (inptr == inend) - break; - } - - if (state == YDecoderState.YPartEqualYPa) { - if (*inptr != (byte) 'r') { - state = YDecoderState.ExpectYBeginNewLine; - continue; - } - - state = YDecoderState.YPartEqualYPar; - octet = *inptr++; - - if (inptr == inend) - break; - } - - if (state == YDecoderState.YPartEqualYPar) { - if (*inptr != (byte) 't') { - state = YDecoderState.ExpectYBeginNewLine; - continue; - } - - state = YDecoderState.YPartEqualYPart; - octet = *inptr++; - - if (inptr == inend) - break; - } - - if (state == YDecoderState.YPartEqualYPart) { - if (*inptr != (byte) ' ') { - state = YDecoderState.ExpectYBeginNewLine; - continue; - } - - state = YDecoderState.ExpectYPartNewLine; - octet = *inptr++; - - if (inptr == inend) - break; - } - - if (state == YDecoderState.ExpectYPartNewLine) { - while (inptr < inend && *inptr != (byte) '\n') - inptr++; - - if (inptr == inend) { - octet = *(inptr - 1); - break; - } - - state = YDecoderState.Payload; - octet = *inptr++; - escaped = false; - eoln = true; - break; - } - } - - return inptr; - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// A pointer to the beginning of the input buffer. - /// The length of the input buffer. - /// A pointer to the beginning of the output buffer. - public unsafe int Decode (byte* input, int length, byte* output) - { - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - - if (state < YDecoderState.Payload) { - if ((inptr = ScanYBeginMarker (inptr, inend)) == inend) - return 0; - } - - if (state == YDecoderState.Ended) - return 0; - - while (inptr < inend) { - octet = *inptr++; - - if (octet == (byte) '\r') { - escaped = false; - continue; - } - - if (octet == (byte) '\n') { - escaped = false; - eoln = true; - continue; - } - - if (escaped) { - if (eoln && octet == (byte) 'y') { - // this can only be =yend - state = YDecoderState.Ended; - break; - } - - escaped = false; - eoln = false; - octet -= 64; - } else if (octet == (byte) '=') { - escaped = true; - continue; - } else { - eoln = false; - } - - octet -= 42; - - crc.Update (octet); - *outptr++ = octet; - } - - return (int) (outptr - output); - } - - /// - /// Decodes the specified input into the output buffer. - /// - /// - /// Decodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// decoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Decode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Decode (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the decoder. - /// - /// - /// Resets the state of the decoder. - /// - public void Reset () - { - octet = (byte) '\n'; - state = initial; - escaped = false; - eoln = true; - - crc.Reset (); - } - } -} diff --git a/src/MimeKit/Encodings/YEncoder.cs b/src/MimeKit/Encodings/YEncoder.cs deleted file mode 100644 index 0873eec..0000000 --- a/src/MimeKit/Encodings/YEncoder.cs +++ /dev/null @@ -1,272 +0,0 @@ -// -// YEncoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; - -namespace MimeKit.Encodings { - /// - /// Incrementally encodes content using the yEnc encoding. - /// - /// - /// The yEncoding is an encoding that is most commonly used with Usenet and - /// is a binary encoding that includes a 32-bit cyclic redundancy check. - /// For more information, see www.yenc.org. - /// - public class YEncoder : IMimeEncoder - { - readonly int lineLength; - byte octets; - Crc32 crc; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new yEnc encoder. - /// - /// The line length to use. - /// - /// is not within the range of 60 to 998. - /// - public YEncoder (int maxLineLength = 128) - { - if (maxLineLength < 60 || maxLineLength > 998) - throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - - lineLength = maxLineLength; - crc = new Crc32 (-1); - Reset (); - } - - /// - /// Gets the checksum. - /// - /// - /// Gets the checksum. - /// - /// The checksum. - public int Checksum { - get { return crc.Checksum; } - } - - /// - /// Clone the with its current state. - /// - /// - /// Creates a new with exactly the same state as the current encoder. - /// - /// A new with identical state. - public IMimeEncoder Clone () - { - var encoder = new YEncoder (lineLength); - - encoder.crc = crc.Clone (); - encoder.octets = octets; - - return encoder; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return ContentEncoding.Default; } - } - - /// - /// Estimates the length of the output. - /// - /// - /// Estimates the number of bytes needed to encode the specified number of input bytes. - /// - /// The estimated output length. - /// The input length. - public int EstimateOutputLength (int inputLength) - { - return (inputLength * 2) + (inputLength / lineLength) + 1; - } - - void ValidateArguments (byte[] input, int startIndex, int length, byte[] output) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (output.Length < EstimateOutputLength (length)) - throw new ArgumentException ("The output buffer is not large enough to contain the encoded input.", nameof (output)); - } - - unsafe int Encode (byte* input, int length, byte* output) - { - byte* inend = input + length; - byte* outptr = output; - byte* inptr = input; - - while (inptr < inend) { - byte c = *inptr++; - - crc.Update (c); - - c += (byte) 42; - - if (c == 0 || c == (byte) '\t' || c == (byte) '\r' || c == (byte) '\n' || c == (byte) '=' || c == (byte) '.') { - *outptr++ = (byte) '='; - *outptr++ = (byte) (c + 64); - octets += 2; - } else { - *outptr++ = c; - octets++; - } - - if (octets >= lineLength) { - *outptr++ = (byte) '\n'; - octets = 0; - } - } - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer. - /// - /// - /// Encodes the specified input into the output buffer. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Encode (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Encode (inptr + startIndex, length, outptr); - } - } - } - - unsafe int Flush (byte* input, int length, byte* output) - { - byte* outptr = output; - - if (length > 0) - outptr += Encode (input, length, output); - - if (octets > 0) { - *outptr++ = (byte) '\n'; - octets = 0; - } - - return (int) (outptr - output); - } - - /// - /// Encodes the specified input into the output buffer, flushing any internal buffer state as well. - /// - /// - /// Encodes the specified input into the output buffer, flusing any internal state as well. - /// The output buffer should be large enough to hold all of the - /// encoded input. For estimating the size needed for the output buffer, - /// see . - /// - /// The number of bytes written to the output buffer. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer. - /// The output buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// is not large enough to contain the encoded content. - /// Use the method to properly determine the - /// necessary length of the byte array. - /// - public int Flush (byte[] input, int startIndex, int length, byte[] output) - { - ValidateArguments (input, startIndex, length, output); - - unsafe { - fixed (byte* inptr = input, outptr = output) { - return Flush (inptr + startIndex, length, outptr); - } - } - } - - /// - /// Resets the encoder. - /// - /// - /// Resets the state of the encoder. - /// - public void Reset () - { - crc.Reset (); - octets = 0; - } - } -} diff --git a/src/MimeKit/FormatOptions.cs b/src/MimeKit/FormatOptions.cs deleted file mode 100644 index 8866754..0000000 --- a/src/MimeKit/FormatOptions.cs +++ /dev/null @@ -1,366 +0,0 @@ -// -// FormatOptions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -using MimeKit.IO.Filters; - -namespace MimeKit { - /// - /// A New-Line format. - /// - /// - /// There are two commonly used line-endings used by modern Operating Systems. - /// Unix-based systems such as Linux and Mac OS use a single character ('\n' aka LF) - /// to represent the end of line where-as Windows (or DOS) uses a sequence of two - /// characters ("\r\n" aka CRLF). Most text-based network protocols such as SMTP, - /// POP3, and IMAP use the CRLF sequence as well. - /// - public enum NewLineFormat : byte { - /// - /// The Unix New-Line format ("\n"). - /// - Unix, - - /// - /// The DOS New-Line format ("\r\n"). - /// - Dos, - - /// - /// A mixed New-Line format where some lines use Unix-based line endings and - /// other lines use DOS-based line endings. - /// - Mixed, - } - - /// - /// Format options for serializing various MimeKit objects. - /// - /// - /// Represents the available options for formatting MIME messages - /// and entities when writing them to a stream. - /// - public class FormatOptions - { - static readonly byte[][] NewLineFormats = { - new byte[] { (byte) '\n' }, new byte[] { (byte) '\r', (byte) '\n' } - }; - - internal const int MaximumLineLength = 998; - internal const int MinimumLineLength = 60; - - const int DefaultMaxLineLength = 78; - - ParameterEncodingMethod parameterEncodingMethod; - bool allowMixedHeaderCharsets; - NewLineFormat newLineFormat; - bool verifyingSignature; - bool ensureNewLine; - bool international; - int maxLineLength; - - /// - /// The default formatting options. - /// - /// - /// If a custom is not passed to methods such as - /// , - /// the default options will be used. - /// - public static readonly FormatOptions Default; - - /// - /// Gets or sets the maximum line length used by the encoders. The encoders - /// use this value to determine where to place line breaks. - /// - /// - /// Specifies the maximum line length to use when line-wrapping headers. - /// - /// The maximum line length. - /// - /// is out of range. It must be between 60 and 998. - /// - /// - /// cannot be changed. - /// - public int MaxLineLength { - get { return maxLineLength; } - set { - if (this == Default) - throw new InvalidOperationException ("The default formatting options cannot be changed."); - - if (value < MinimumLineLength || value > MaximumLineLength) - throw new ArgumentOutOfRangeException (nameof (value)); - - maxLineLength = value; - } - } - - /// - /// Get or set the new-line format. - /// - /// - /// Specifies the new-line encoding to use when writing the message - /// or entity to a stream. - /// - /// The new-line format. - /// - /// is not a valid . - /// - /// - /// cannot be changed. - /// - public NewLineFormat NewLineFormat { - get { return newLineFormat; } - set { - if (this == Default) - throw new InvalidOperationException ("The default formatting options cannot be changed."); - - switch (newLineFormat) { - case NewLineFormat.Unix: - case NewLineFormat.Dos: - newLineFormat = value; - break; - default: - throw new ArgumentOutOfRangeException (nameof (value)); - } - } - } - - /// - /// Get or set whether the formatter should ensure that messages end with a new-line sequence. - /// - /// - /// By default, when writing a to a stream, the serializer attempts to - /// maintain byte-for-byte compatibility with the original stream that the message was parsed from. - /// This means that if the ogirinal message stream did not end with a new-line sequence, then the - /// output of writing the message back to a stream will also not end with a new-line sequence. - /// To override this behavior, you can set this property to true in order to ensure - /// that writing the message back to a stream will always end with a new-line sequence. - /// - /// true in order to ensure that the message will end with a new-line sequence; otherwise, false. - /// - /// cannot be changed. - /// - public bool EnsureNewLine { - get { return ensureNewLine; } - set { - if (this == Default) - throw new InvalidOperationException ("The default formatting options cannot be changed."); - - ensureNewLine = value; - } - } - - internal IMimeFilter CreateNewLineFilter (bool ensureNewLine = false) - { - switch (NewLineFormat) { - case NewLineFormat.Unix: - return new Dos2UnixFilter (ensureNewLine); - default: - return new Unix2DosFilter (ensureNewLine); - } - } - - internal string NewLine { - get { return NewLineFormat == NewLineFormat.Unix ? "\n" : "\r\n"; } - } - - internal byte[] NewLineBytes { - get { return NewLineFormats[(int) NewLineFormat]; } - } - - internal bool VerifyingSignature { - get { return verifyingSignature; } - set { verifyingSignature = value; } - } - - /// - /// Get the message headers that should be hidden. - /// - /// - /// Specifies the set of headers that should be removed when - /// writing a to a stream. - /// This is primarily meant for the purposes of removing Bcc - /// and Resent-Bcc headers when sending via a transport such as - /// SMTP. - /// - /// The message headers. - public HashSet HiddenHeaders { - get; private set; - } - - /// - /// Get or set whether the new "Internationalized Email" formatting standards should be used. - /// - /// - /// The new "Internationalized Email" format is defined by - /// rfc6530 and - /// rfc6532. - /// This feature should only be used when formatting messages meant to be sent via - /// SMTP using the SMTPUTF8 extension (rfc6531) - /// or when appending messages to an IMAP folder via UTF8 APPEND - /// (rfc6855). - /// - /// true if the new internationalized formatting should be used; otherwise, false. - /// - /// cannot be changed. - /// - public bool International { - get { return international; } - set { - if (this == Default) - throw new InvalidOperationException ("The default formatting options cannot be changed."); - - international = value; - } - } - - /// - /// Get or set whether the formatter should allow mixed charsets in the headers. - /// - /// - /// When this option is enabled, the MIME formatter will try to use us-ascii and/or - /// iso-8859-1 to encode headers when appropriate rather than being forced to use the - /// specified charset for all encoded-word tokens in order to maximize readability. - /// Unfortunately, mail clients like Outlook and Thunderbird do not treat - /// encoded-word tokens individually and assume that all tokens are encoded using the - /// charset declared in the first encoded-word token despite the specification - /// explicitly stating that each encoded-word token should be treated independently. - /// The Thunderbird bug can be tracked at - /// - /// https://bugzilla.mozilla.org/show_bug.cgi?id=317263. - /// - /// true if the formatter should be allowed to use us-ascii and/or iso-8859-1 when encoding headers; otherwise, false. - public bool AllowMixedHeaderCharsets { - get { return allowMixedHeaderCharsets; } - set { - if (this == Default) - throw new InvalidOperationException ("The default formatting options cannot be changed."); - - allowMixedHeaderCharsets = value; - } - } - - /// - /// Get or set the method to use for encoding Content-Type and Content-Disposition parameter values. - /// - /// - /// The method to use for encoding Content-Type and Content-Disposition parameter - /// values when the is set to - /// . - /// The MIME specifications specify that the proper method for encoding Content-Type - /// and Content-Disposition parameter values is the method described in - /// rfc2231. However, it is common for - /// some older email clients to improperly encode using the method described in - /// rfc2047 instead. - /// - /// The parameter encoding method that will be used. - /// - /// is not a valid value. - /// - public ParameterEncodingMethod ParameterEncodingMethod { - get { return parameterEncodingMethod; } - set { - if (this == Default) - throw new InvalidOperationException ("The default formatting options cannot be changed."); - - switch (value) { - case ParameterEncodingMethod.Rfc2047: - case ParameterEncodingMethod.Rfc2231: - parameterEncodingMethod = value; - break; - default: - throw new ArgumentOutOfRangeException (nameof (value)); - } - } - } - - static FormatOptions () - { - Default = new FormatOptions (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new set of formatting options for use with methods such as - /// . - /// - public FormatOptions () - { - HiddenHeaders = new HashSet (); - parameterEncodingMethod = ParameterEncodingMethod.Rfc2231; - maxLineLength = DefaultMaxLineLength; - allowMixedHeaderCharsets = false; - ensureNewLine = false; - international = false; - - if (Environment.NewLine.Length == 1) - newLineFormat = NewLineFormat.Unix; - else - newLineFormat = NewLineFormat.Dos; - } - - /// - /// Clones an instance of . - /// - /// - /// Clones the formatting options. - /// - /// An exact copy of the . - public FormatOptions Clone () - { - var options = new FormatOptions (); - options.maxLineLength = maxLineLength; - options.newLineFormat = newLineFormat; - options.ensureNewLine = ensureNewLine; - options.HiddenHeaders = new HashSet (HiddenHeaders); - options.allowMixedHeaderCharsets = allowMixedHeaderCharsets; - options.parameterEncodingMethod = parameterEncodingMethod; - options.verifyingSignature = verifyingSignature; - options.international = international; - return options; - } - - /// - /// Get the default formatting options in a thread-safe way. - /// - /// - /// Gets the default formatting options in a thread-safe way. - /// - /// The default formatting options. - internal static FormatOptions CloneDefault () - { - lock (Default) { - return Default.Clone (); - } - } - } -} diff --git a/src/MimeKit/GroupAddress.cs b/src/MimeKit/GroupAddress.cs deleted file mode 100644 index 3f29dc4..0000000 --- a/src/MimeKit/GroupAddress.cs +++ /dev/null @@ -1,747 +0,0 @@ -// -// GroupAddress.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Text; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// An address group, as specified by rfc0822. - /// - /// - /// Group addresses are rarely used anymore. Typically, if you see a group address, - /// it will be of the form: "undisclosed-recipients: ;". - /// - public class GroupAddress : InternetAddress - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified name and list of addresses. The - /// specified text encoding is used when encoding the name according to the rules of rfc2047. - /// - /// The character encoding to be used for encoding the name. - /// The name of the group. - /// A list of addresses. - /// - /// is null. - /// - public GroupAddress (Encoding encoding, string name, IEnumerable addresses) : base (encoding, name) - { - Members = new InternetAddressList (addresses); - Members.Changed += MembersChanged; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified name and list of addresses. - /// - /// The name of the group. - /// A list of addresses. - public GroupAddress (string name, IEnumerable addresses) : this (Encoding.UTF8, name, addresses) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified name. The specified - /// text encoding is used when encoding the name according to the rules of rfc2047. - /// - /// The character encoding to be used for encoding the name. - /// The name of the group. - /// - /// is null. - /// - public GroupAddress (Encoding encoding, string name) : base (encoding, name) - { - Members = new InternetAddressList (); - Members.Changed += MembersChanged; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified name. - /// - /// The name of the group. - public GroupAddress (string name) : this (Encoding.UTF8, name) - { - } - - /// - /// Clone the group address. - /// - /// - /// Clones the group address. - /// - /// The cloned group address. - public override InternetAddress Clone () - { - return new GroupAddress (Encoding, Name, Members.Select (x => x.Clone ())); - } - - /// - /// Gets the members of the group. - /// - /// - /// Represents the member addresses of the group. Typically the member addresses - /// will be of the variety, but it is possible - /// for groups to contain other groups. - /// - /// The list of members. - public InternetAddressList Members { - get; private set; - } - - internal override void Encode (FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength) - { - if (!string.IsNullOrEmpty (Name)) { - string name; - - if (!options.International) { - var encoded = Rfc2047.EncodePhrase (options, Encoding, Name); - name = Encoding.ASCII.GetString (encoded, 0, encoded.Length); - } else { - name = EncodeInternationalizedPhrase (Name); - } - - if (lineLength + name.Length > options.MaxLineLength) { - if (name.Length > options.MaxLineLength) { - // we need to break up the name... - builder.AppendFolded (options, firstToken, name, ref lineLength); - } else { - // the name itself is short enough to fit on a single line, - // but only if we write it on a line by itself - if (!firstToken && lineLength > 1) { - builder.LineWrap (options); - lineLength = 1; - } - - lineLength += name.Length; - builder.Append (name); - } - } else { - // we can safely fit the name on this line... - lineLength += name.Length; - builder.Append (name); - } - } - - builder.Append (": "); - lineLength += 2; - - Members.Encode (options, builder, false, ref lineLength); - - builder.Append (';'); - lineLength++; - } - - /// - /// Returns a string representation of the , - /// optionally encoding it for transport. - /// - /// - /// Returns a string containing the formatted group of addresses. If the - /// parameter is true, then the name of the group and all member addresses will be encoded - /// according to the rules defined in rfc2047, otherwise the names will not be encoded at all and - /// will therefor only be suitable for display purposes. - /// - /// A string representing the . - /// The formatting options. - /// If set to true, the will be encoded. - /// - /// is null. - /// - public override string ToString (FormatOptions options, bool encode) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - var builder = new StringBuilder (); - - if (encode) { - int lineLength = 0; - - Encode (options, builder, true, ref lineLength); - } else { - builder.Append (Name); - builder.Append (':'); - builder.Append (' '); - - for (int i = 0; i < Members.Count; i++) { - if (i > 0) - builder.Append (", "); - - builder.Append (Members[i]); - } - - builder.Append (';'); - } - - return builder.ToString (); - } - - #region IEquatable implementation - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Compares two group addresses to determine if they are identical or not. - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public override bool Equals (InternetAddress other) - { - var group = other as GroupAddress; - - if (group == null) - return false; - - return Name == group.Name && Members.Equals (group.Members); - } - - #endregion - - void MembersChanged (object sender, EventArgs e) - { - OnChanged (); - } - - static bool TryParse (ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out GroupAddress group) - { - var flags = AddressParserFlags.AllowGroupAddress; - InternetAddress address; - - if (throwOnError) - flags |= AddressParserFlags.ThrowOnError; - - if (!InternetAddress.TryParse (options, text, ref index, endIndex, 0, flags, out address)) { - group = null; - return false; - } - - group = (GroupAddress) address; - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed group address. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out GroupAddress group) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int endIndex = startIndex + length; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, false, out group)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - group = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed group address. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out GroupAddress group) - { - return TryParse (ParserOptions.Default, buffer, startIndex, length, out group); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed group address. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, out GroupAddress group) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int endIndex = buffer.Length; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, false, out group)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - group = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed group address. - /// - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (byte[] buffer, int startIndex, out GroupAddress group) - { - return TryParse (ParserOptions.Default, buffer, startIndex, out group); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The parsed group address. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, out GroupAddress group) - { - ParseUtils.ValidateArguments (options, buffer); - - int endIndex = buffer.Length; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, false, out group)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - group = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The parsed group address. - /// - /// is null. - /// - public static bool TryParse (byte[] buffer, out GroupAddress group) - { - return TryParse (ParserOptions.Default, buffer, out group); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The text. - /// The parsed group address. - /// - /// is null. - /// - public static bool TryParse (ParserOptions options, string text, out GroupAddress group) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (text == null) - throw new ArgumentNullException (nameof (text)); - - var buffer = Encoding.UTF8.GetBytes (text); - int endIndex = buffer.Length; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, false, out group)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - group = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The text. - /// The parsed group address. - /// - /// is null. - /// - public static bool TryParse (string text, out GroupAddress group) - { - return TryParse (ParserOptions.Default, text, out group); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// could not be parsed. - /// - public static new GroupAddress Parse (ParserOptions options, byte[] buffer, int startIndex, int length) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int endIndex = startIndex + length; - int index = startIndex; - GroupAddress group; - - if (!TryParse (options, buffer, ref index, endIndex, true, out group)) - throw new ParseException ("No group address found.", startIndex, startIndex); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return group; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// could not be parsed. - /// - public static new GroupAddress Parse (byte[] buffer, int startIndex, int length) - { - return Parse (ParserOptions.Default, buffer, startIndex, length); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// could not be parsed. - /// - public static new GroupAddress Parse (ParserOptions options, byte[] buffer, int startIndex) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int endIndex = buffer.Length; - int index = startIndex; - GroupAddress group; - - if (!TryParse (options, buffer, ref index, endIndex, true, out group)) - throw new ParseException ("No group address found.", startIndex, startIndex); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return group; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// The starting index of the input buffer. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// could not be parsed. - /// - public static new GroupAddress Parse (byte[] buffer, int startIndex) - { - return Parse (ParserOptions.Default, buffer, startIndex); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// could not be parsed. - /// - public static new GroupAddress Parse (ParserOptions options, byte[] buffer) - { - ParseUtils.ValidateArguments (options, buffer); - - int endIndex = buffer.Length; - GroupAddress group; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, true, out group)) - throw new ParseException ("No group address found.", 0, 0); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return group; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public static new GroupAddress Parse (byte[] buffer) - { - return Parse (ParserOptions.Default, buffer); - } - - /// - /// Parse the given text into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The text. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// could not be parsed. - /// - public static new GroupAddress Parse (ParserOptions options, string text) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (text == null) - throw new ArgumentNullException (nameof (text)); - - var buffer = Encoding.UTF8.GetBytes (text); - int endIndex = buffer.Length; - GroupAddress group; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, true, out group)) - throw new ParseException ("No group address found.", 0, 0); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return group; - } - - /// - /// Parse the given text into a new instance. - /// - /// - /// Parses a single . If the address is not a group address or - /// there is more than a single group address, then parsing will fail. - /// - /// The parsed . - /// The text. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public static new GroupAddress Parse (string text) - { - return Parse (ParserOptions.Default, text); - } - } -} diff --git a/src/MimeKit/Header.cs b/src/MimeKit/Header.cs deleted file mode 100644 index 24b6085..0000000 --- a/src/MimeKit/Header.cs +++ /dev/null @@ -1,1582 +0,0 @@ -// -// Header.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections.Generic; - -using MimeKit.Utils; -using MimeKit.Cryptography; - -namespace MimeKit { - /// - /// A class representing a Message or MIME header. - /// - /// - /// Represents a single header field and value pair. - /// - public class Header - { - internal static readonly byte[] Colon = { (byte) ':' }; - internal readonly ParserOptions Options; - - // cached FormatOptions that change the way the header is formatted - //bool allowMixedHeaderCharsets = FormatOptions.Default.AllowMixedHeaderCharsets; - //NewLineFormat newLineFormat = FormatOptions.Default.NewLineFormat; - //bool international = FormatOptions.Default.International; - //Encoding charset = CharsetUtils.UTF8; - - readonly byte[] rawField; - bool explicitRawValue; - string textValue; - byte[] rawValue; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header for the specified field and - /// value pair. The encoding is used to determine which charset to use - /// when encoding the value according to the rules of rfc2047. - /// - /// The character encoding that should be used to - /// encode the header value. - /// The header identifier. - /// The value of the header. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid . - /// - public Header (Encoding encoding, HeaderId id, string value) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Options = ParserOptions.Default.Clone (); - Field = id.ToHeaderName (); - Id = id; - - rawField = Encoding.ASCII.GetBytes (Field); - SetValue (encoding, value); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header for the specified field and - /// value pair. The encoding is used to determine which charset to use - /// when encoding the value according to the rules of rfc2047. - /// - /// The charset that should be used to encode the - /// header value. - /// The header identifier. - /// The value of the header. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid . - /// - /// - /// is not supported. - /// - public Header (string charset, HeaderId id, string value) - { - if (charset == null) - throw new ArgumentNullException (nameof (charset)); - - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - var encoding = CharsetUtils.GetEncoding (charset); - Options = ParserOptions.Default.Clone (); - Field = id.ToHeaderName (); - Id = id; - - rawField = Encoding.ASCII.GetBytes (Field); - SetValue (encoding, value); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header for the specified field and - /// value pair with the UTF-8 encoding. - /// - /// The header identifier. - /// The value of the header. - /// - /// is null. - /// - /// - /// is not a valid . - /// - public Header (HeaderId id, string value) : this (Encoding.UTF8, id, value) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header for the specified field and - /// value pair. The encoding is used to determine which charset to use - /// when encoding the value according to the rules of rfc2047. - /// - /// The character encoding that should be used - /// to encode the header value. - /// The name of the header field. - /// The value of the header. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - public Header (Encoding encoding, string field, string value) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (field == null) - throw new ArgumentNullException (nameof (field)); - - if (field.Length == 0) - throw new ArgumentException ("Header field names are not allowed to be empty.", nameof (field)); - - for (int i = 0; i < field.Length; i++) { - if (field[i] >= 127 || !IsAsciiAtom ((byte) field[i])) - throw new ArgumentException ("Illegal characters in header field name.", nameof (field)); - } - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Options = ParserOptions.Default.Clone (); - Id = field.ToHeaderId (); - Field = field; - - rawField = Encoding.ASCII.GetBytes (field); - SetValue (encoding, value); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header for the specified field and - /// value pair. The encoding is used to determine which charset to use - /// when encoding the value according to the rules of rfc2047. - /// - /// The charset that should be used to encode the - /// header value. - /// The name of the header field. - /// The value of the header. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - /// - /// is not supported. - /// - public Header (string charset, string field, string value) - { - if (charset == null) - throw new ArgumentNullException (nameof (charset)); - - if (field == null) - throw new ArgumentNullException (nameof (field)); - - if (field.Length == 0) - throw new ArgumentException ("Header field names are not allowed to be empty.", nameof (field)); - - for (int i = 0; i < field.Length; i++) { - if (field[i] >= 127 || !IsAsciiAtom ((byte) field[i])) - throw new ArgumentException ("Illegal characters in header field name.", nameof (field)); - } - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - var encoding = CharsetUtils.GetEncoding (charset); - Options = ParserOptions.Default.Clone (); - Id = field.ToHeaderId (); - Field = field; - - rawField = Encoding.ASCII.GetBytes (field); - SetValue (encoding, value); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header for the specified field and - /// value pair with the UTF-8 encoding. - /// - /// The name of the header field. - /// The value of the header. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - public Header (string field, string value) : this (Encoding.UTF8, field, value) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header with the specified values. - /// This constructor is used by . - /// - /// The parser options used. - /// The id of the header. - /// The name of the header field. - /// The raw header field. - /// The raw value of the header. - protected Header (ParserOptions options, HeaderId id, string name, byte[] field, byte[] value) - { - Options = options; - rawField = field; - rawValue = value; - Field = name; - Id = id; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header with the specified raw values. - /// This constructor is used by the - /// TryParse methods. - /// - /// The parser options used. - /// The raw header field. - /// The raw value of the header. - /// true if the header field is invalid; othereise, false. - internal protected Header (ParserOptions options, byte[] field, byte[] value, bool invalid) - { - var chars = new char[field.Length]; - int count = 0; - - while (count < field.Length && (invalid || !field[count].IsBlank ())) { - chars[count] = (char) field[count]; - count++; - } - - Options = options; - rawField = field; - rawValue = value; - - Field = new string (chars, 0, count); - Id = Field.ToHeaderId (); - IsInvalid = invalid; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message or entity header with the specified raw values. - /// This constructor is used by and - /// when serializing new values for headers. - /// - /// The parser options used. - /// The id of the header. - /// The raw header field. - /// The raw value of the header. - internal protected Header (ParserOptions options, HeaderId id, string field, byte[] value) - { - Options = options; - rawField = Encoding.ASCII.GetBytes (field); - rawValue = value; - Field = field; - Id = id; - } - - /// - /// Clone the header. - /// - /// - /// Clones the header, copying the current RawValue. - /// - /// A copy of the header with its current state. - public Header Clone () - { - var header = new Header (Options, Id, Field, rawField, rawValue) { - explicitRawValue = explicitRawValue, - IsInvalid = IsInvalid - }; - - // if the textValue has already been calculated, set it on the cloned header as well. - header.textValue = textValue; - - return header; - } - - /// - /// Gets the stream offset of the beginning of the header. - /// - /// - /// If the offset is set, it refers to the byte offset where it - /// was found in the stream it was parsed from. - /// - /// The stream offset. - public long? Offset { - get; internal set; - } - - /// - /// Gets the name of the header field. - /// - /// - /// Represents the field name of the header. - /// - /// The name of the header field. - public string Field { - get; private set; - } - - /// - /// Gets the header identifier. - /// - /// - /// This property is mainly used for switch-statements for performance reasons. - /// - /// The header identifier. - public HeaderId Id { - get; private set; - } - - internal bool IsInvalid { - get; private set; - } - - /// - /// Gets the raw field name of the header. - /// - /// - /// Contains the raw field name of the header. - /// - /// The raw field name of the header. - public byte[] RawField { - get { return rawField; } - } - - /// - /// Gets the raw value of the header. - /// - /// - /// Contains the raw value of the header, before any decoding or charset conversion. - /// - /// The raw value of the header. - public byte[] RawValue { - get { return rawValue; } - } - - /// - /// Gets or sets the header value. - /// - /// - /// Represents the decoded header value and is suitable for displaying to the user. - /// - /// The header value. - /// - /// is null. - /// - public string Value { - get { - if (textValue == null) - textValue = Unfold (Rfc2047.DecodeText (Options, rawValue)); - - return textValue; - } - set { - SetValue (FormatOptions.Default, Encoding.UTF8, value); - } - } - - /// - /// Gets the header value using the specified character encoding. - /// - /// - /// If the raw header value does not properly encode non-ASCII text, the decoder - /// will fall back to a default charset encoding. Sometimes, however, this - /// default charset fallback is wrong and the mail client may wish to override - /// that default charset on a per-header basis. - /// By using this method, the client is able to override the fallback charset - /// on a per-header basis. - /// - /// The value. - /// The character encoding to use as a fallback. - public string GetValue (Encoding encoding) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - var options = Options.Clone (); - options.CharsetEncoding = encoding; - - return Unfold (Rfc2047.DecodeText (options, rawValue)); - } - - /// - /// Gets the header value using the specified charset. - /// - /// - /// If the raw header value does not properly encode non-ASCII text, the decoder - /// will fall back to a default charset encoding. Sometimes, however, this - /// default charset fallback is wrong and the mail client may wish to override - /// that default charset on a per-header basis. - /// By using this method, the client is able to override the fallback charset - /// on a per-header basis. - /// - /// The value. - /// The charset to use as a fallback. - public string GetValue (string charset) - { - if (charset == null) - throw new ArgumentNullException (nameof (charset)); - - var encoding = CharsetUtils.GetEncoding (charset); - - return GetValue (encoding); - } - - static byte[] EncodeAddressHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - var encoded = new StringBuilder (" "); - int lineLength = field.Length + 2; - InternetAddressList list; - - if (!InternetAddressList.TryParse (options, value, out list)) - return (byte[]) format.NewLineBytes.Clone (); - - list.Encode (format, encoded, true, ref lineLength); - encoded.Append (format.NewLine); - - if (format.International) - return Encoding.UTF8.GetBytes (encoded.ToString ()); - - return Encoding.ASCII.GetBytes (encoded.ToString ()); - } - - static byte[] EncodeMessageIdHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - return encoding.GetBytes (" " + value + format.NewLine); - } - - delegate void ReceivedTokenSkipValueFunc (byte[] text, ref int index); - - static void ReceivedTokenSkipAtom (byte[] text, ref int index) - { - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, text.Length, false) || index >= text.Length) - return; - - ParseUtils.SkipAtom (text, ref index, text.Length); - } - - static void ReceivedTokenSkipDomain (byte[] text, ref int index) - { - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, text.Length, false)) - return; - - if (text[index] == (byte) '[') { - while (index < text.Length && text[index] != (byte) ']') - index++; - - if (index < text.Length) - index++; - - return; - } - - while (ParseUtils.SkipAtom (text, ref index, text.Length) && index < text.Length && text[index] == (byte) '.') - index++; - } - - static readonly byte[] ReceivedAddrSpecSentinels = { (byte) '>', (byte) ';' }; - static readonly byte[] ReceivedMessageIdSentinels = { (byte) '>' }; - - static void ReceivedTokenSkipAddress (byte[] text, ref int index) - { - string addrspec; - int at; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, text.Length, false) || index >= text.Length) - return; - - if (text[index] == (byte) '<') - index++; - - InternetAddress.TryParseAddrspec (text, ref index, text.Length, ReceivedAddrSpecSentinels, false, out addrspec, out at); - - if (index < text.Length && text[index] == (byte) '>') - index++; - } - - static void ReceivedTokenSkipMessageId (byte[] text, ref int index) - { - string addrspec; - int at; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, text.Length, false) || index >= text.Length) - return; - - if (text[index] == (byte) '<') { - index++; - - InternetAddress.TryParseAddrspec (text, ref index, text.Length, ReceivedMessageIdSentinels, false, out addrspec, out at); - - if (index < text.Length && text[index] == (byte) '>') - index++; - } else { - ParseUtils.SkipAtom (text, ref index, text.Length); - } - } - - struct ReceivedToken { - public readonly ReceivedTokenSkipValueFunc Skip; - public readonly string Atom; - - public ReceivedToken (string atom, ReceivedTokenSkipValueFunc skip) - { - Atom = atom; - Skip = skip; - } - } - - static readonly ReceivedToken[] ReceivedTokens = { - new ReceivedToken ("from", ReceivedTokenSkipDomain), - new ReceivedToken ("by", ReceivedTokenSkipDomain), - new ReceivedToken ("via", ReceivedTokenSkipDomain), - new ReceivedToken ("with", ReceivedTokenSkipAtom), - new ReceivedToken ("id", ReceivedTokenSkipMessageId), - new ReceivedToken ("for", ReceivedTokenSkipAddress), - }; - - class ReceivedTokenValue - { - public readonly int StartIndex; - public readonly int Length; - - public ReceivedTokenValue (int startIndex, int length) - { - StartIndex = startIndex; - Length = length; - } - } - - static byte[] EncodeReceivedHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - var tokens = new List (); - var rawValue = encoding.GetBytes (value); - var encoded = new StringBuilder (); - int lineLength = field.Length + 1; - bool date = false; - int index = 0; - int count = 0; - - while (index < rawValue.Length) { - ReceivedTokenValue token = null; - int startIndex = index; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (rawValue, ref index, rawValue.Length, false) || index >= rawValue.Length) { - tokens.Add (new ReceivedTokenValue (startIndex, index - startIndex)); - break; - } - - while (index < rawValue.Length && !rawValue[index].IsWhitespace ()) - index++; - - var atom = encoding.GetString (rawValue, startIndex, index - startIndex); - - for (int i = 0; i < ReceivedTokens.Length; i++) { - if (atom == ReceivedTokens[i].Atom) { - ReceivedTokens[i].Skip (rawValue, ref index); - - if (ParseUtils.SkipCommentsAndWhiteSpace (rawValue, ref index, rawValue.Length, false)) { - if (index < rawValue.Length && rawValue[index] == (byte) ';') { - date = true; - index++; - } - } - - token = new ReceivedTokenValue (startIndex, index - startIndex); - break; - } - } - - if (token == null) { - if (ParseUtils.SkipCommentsAndWhiteSpace (rawValue, ref index, rawValue.Length, false)) { - while (index < rawValue.Length && !rawValue[index].IsWhitespace ()) - index++; - } - - token = new ReceivedTokenValue (startIndex, index - startIndex); - } - - tokens.Add (token); - - ParseUtils.SkipWhiteSpace (rawValue, ref index, rawValue.Length); - - if (date && index < rawValue.Length) { - // slurp up the date (the final token) - tokens.Add (new ReceivedTokenValue (index, rawValue.Length - index)); - break; - } - } - - foreach (var token in tokens) { - var text = encoding.GetString (rawValue, token.StartIndex, token.Length).TrimEnd (); - - if (count > 0 && lineLength + text.Length + 1 > format.MaxLineLength) { - encoded.Append (format.NewLine); - encoded.Append ('\t'); - lineLength = 1; - count = 0; - } else { - encoded.Append (' '); - lineLength++; - } - - lineLength += text.Length; - encoded.Append (text); - count++; - } - - encoded.Append (format.NewLine); - - return encoding.GetBytes (encoded.ToString ()); - } - - static byte[] EncodeAuthenticationResultsHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - var buffer = Encoding.UTF8.GetBytes (value); - - if (!AuthenticationResults.TryParse (buffer, out AuthenticationResults authres)) - return EncodeUnstructuredHeader (options, format, encoding, field, value); - - var encoded = new StringBuilder (); - int lineLength = field.Length + 1; - - authres.Encode (format, encoded, lineLength); - - return encoding.GetBytes (encoded.ToString ()); - } - - static void EncodeDkimLongValue (FormatOptions format, StringBuilder encoded, ref int lineLength, string value) - { - int startIndex = 0; - - do { - int lineLeft = format.MaxLineLength - lineLength; - int index = Math.Min (startIndex + lineLeft, value.Length); - - encoded.Append (value.Substring (startIndex, index - startIndex)); - lineLength += (index - startIndex); - - if (index == value.Length) - break; - - encoded.Append (format.NewLine); - encoded.Append ('\t'); - lineLength = 1; - - startIndex = index; - } while (true); - } - - static void EncodeDkimHeaderList (FormatOptions format, StringBuilder encoded, ref int lineLength, string value, char delim) - { - var tokens = value.Split (delim); - - for (int i = 0; i < tokens.Length; i++) { - if (i > 0) { - encoded.Append (delim); - lineLength++; - } - - if (lineLength + tokens[i].Length + 1 > format.MaxLineLength) { - encoded.Append (format.NewLine); - encoded.Append ('\t'); - lineLength = 1; - - if (tokens[i].Length + 1 > format.MaxLineLength) { - EncodeDkimLongValue (format, encoded, ref lineLength, tokens[i]); - } else { - lineLength += tokens[i].Length; - encoded.Append (tokens[i]); - } - } else { - lineLength += tokens[i].Length; - encoded.Append (tokens[i]); - } - } - } - - static byte[] EncodeDkimOrArcSignatureHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - var encoded = new StringBuilder (); - int lineLength = field.Length + 1; - var token = new StringBuilder (); - int index = 0; - - while (index < value.Length) { - while (index < value.Length && IsWhiteSpace (value[index])) - index++; - - int startIndex = index; - string name; - - while (index < value.Length && value[index] != '=') { - if (!IsWhiteSpace (value[index])) - token.Append (value[index]); - index++; - } - - name = value.Substring (startIndex, index - startIndex); - - while (index < value.Length && value[index] != ';') { - if (!IsWhiteSpace (value[index])) - token.Append (value[index]); - index++; - } - - if (index < value.Length && value[index] == ';') { - token.Append (';'); - index++; - } - - if (lineLength + token.Length + 1 > format.MaxLineLength || name == "bh" || name == "b") { - encoded.Append (format.NewLine); - encoded.Append ('\t'); - lineLength = 1; - } else { - encoded.Append (' '); - lineLength++; - } - - if (token.Length > format.MaxLineLength) { - switch (name) { - case "z": - EncodeDkimHeaderList (format, encoded, ref lineLength, token.ToString (), '|'); - break; - case "h": - EncodeDkimHeaderList (format, encoded, ref lineLength, token.ToString (), ':'); - break; - default: - EncodeDkimLongValue (format, encoded, ref lineLength, token.ToString ()); - break; - } - } else { - encoded.Append (token.ToString ()); - lineLength += token.Length; - } - - token.Length = 0; - } - - encoded.Append (format.NewLine); - - return encoding.GetBytes (encoded.ToString ()); - } - - static byte[] EncodeReferencesHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - var encoded = new StringBuilder (); - int lineLength = field.Length + 1; - int count = 0; - - foreach (var reference in MimeUtils.EnumerateReferences (value)) { - if (count > 0 && lineLength + reference.Length + 3 > format.MaxLineLength) { - encoded.Append (format.NewLine); - encoded.Append ('\t'); - lineLength = 1; - count = 0; - } else { - encoded.Append (' '); - lineLength++; - } - - encoded.Append ('<').Append (reference).Append ('>'); - lineLength += reference.Length + 2; - count++; - } - - encoded.Append (format.NewLine); - - return encoding.GetBytes (encoded.ToString ()); - } - - static bool IsWhiteSpace (char c) - { - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; - } - - static IEnumerable TokenizeText (string text) - { - int index = 0; - - while (index < text.Length) { - int startIndex = index; - - while (index < text.Length && !IsWhiteSpace (text[index])) - index++; - - yield return text.Substring (startIndex, index - startIndex); - - if (index == text.Length) - break; - - startIndex = index; - - while (index < text.Length && IsWhiteSpace (text[index])) - index++; - - yield return text.Substring (startIndex, index - startIndex); - } - - yield break; - } - - class BrokenWord - { - public readonly char[] Text; - public readonly int StartIndex; - public readonly int Length; - - public BrokenWord (char[] text, int startIndex, int length) - { - StartIndex = startIndex; - Length = length; - Text = text; - } - } - - static IEnumerable WordBreak (FormatOptions format, string word, int lineLength) - { - var chars = word.ToCharArray (); - int startIndex = 0; - - lineLength = Math.Max (lineLength, 1); - - while (startIndex < word.Length) { - int length = Math.Min (format.MaxLineLength - lineLength, word.Length - startIndex); - - if (char.IsSurrogatePair (word, startIndex + length - 1)) - length--; - - yield return new BrokenWord (chars, startIndex, length); - - startIndex += length; - lineLength = 1; - } - - yield break; - } - - internal static string Fold (FormatOptions format, string field, string value) - { - var folded = new StringBuilder (value.Length); - int lineLength = field.Length + 2; - int lastLwsp = -1; - - folded.Append (' '); - - var words = TokenizeText (value); - - foreach (var word in words) { - if (IsWhiteSpace (word[0])) { - if (lineLength + word.Length > format.MaxLineLength) { - for (int i = 0; i < word.Length; i++) { - if (lineLength > format.MaxLineLength) { - folded.Append (format.NewLine); - lineLength = 0; - } - - folded.Append (word[i]); - lineLength++; - } - } else { - lineLength += word.Length; - folded.Append (word); - } - - lastLwsp = folded.Length - 1; - continue; - } - - if (lastLwsp != -1 && lineLength + word.Length > format.MaxLineLength) { - folded.Insert (lastLwsp, format.NewLine); - lineLength = 1; - lastLwsp = -1; - } - - if (word.Length > format.MaxLineLength) { - foreach (var broken in WordBreak (format, word, lineLength)) { - if (lineLength + broken.Length > format.MaxLineLength) { - folded.Append (format.NewLine); - folded.Append (' '); - lineLength = 1; - } - - folded.Append (broken.Text, broken.StartIndex, broken.Length); - lineLength += broken.Length; - } - } else { - lineLength += word.Length; - folded.Append (word); - } - } - - folded.Append (format.NewLine); - - return folded.ToString (); - } - - static byte[] EncodeContentDisposition (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - var disposition = ContentDisposition.Parse (options, value); - var encoded = disposition.Encode (format, encoding); - - return Encoding.UTF8.GetBytes (encoded); - } - - static byte[] EncodeContentType (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - var contentType = ContentType.Parse (options, value); - var encoded = contentType.Encode (format, encoding); - - return Encoding.UTF8.GetBytes (encoded); - } - - static byte[] EncodeUnstructuredHeader (ParserOptions options, FormatOptions format, Encoding encoding, string field, string value) - { - if (format.International) { - var folded = Fold (format, field, value); - - return Encoding.UTF8.GetBytes (folded); - } - - var encoded = Rfc2047.EncodeText (format, encoding, value); - - return Rfc2047.FoldUnstructuredHeader (format, field, encoded); - } - - /// - /// Format the raw value of the header to conform with the specified formatting options. - /// - /// - /// This method will called by the SetValue - /// methods and may also be conditionally called when the header is being written to a - /// . - /// - /// The formatting options. - /// The character encoding to be used. - /// The decoded (and unfolded) header value. - /// A byte array containing the raw header value that should be written. - protected virtual byte[] FormatRawValue (FormatOptions format, Encoding encoding, string value) - { - switch (Id) { - case HeaderId.DispositionNotificationTo: - case HeaderId.ResentReplyTo: - case HeaderId.ResentSender: - case HeaderId.ResentFrom: - case HeaderId.ResentBcc: - case HeaderId.ResentCc: - case HeaderId.ResentTo: - case HeaderId.ReplyTo: - case HeaderId.Sender: - case HeaderId.From: - case HeaderId.Bcc: - case HeaderId.Cc: - case HeaderId.To: - return EncodeAddressHeader (Options, format, encoding, Field, value); - case HeaderId.Received: - return EncodeReceivedHeader (Options, format, encoding, Field, value); - case HeaderId.ResentMessageId: - case HeaderId.InReplyTo: - case HeaderId.MessageId: - case HeaderId.ContentId: - return EncodeMessageIdHeader (Options, format, encoding, Field, value); - case HeaderId.References: - return EncodeReferencesHeader (Options, format, encoding, Field, value); - case HeaderId.ContentDisposition: - return EncodeContentDisposition (Options, format, encoding, Field, value); - case HeaderId.ContentType: - return EncodeContentType (Options, format, encoding, Field, value); - case HeaderId.ArcAuthenticationResults: - case HeaderId.AuthenticationResults: - return EncodeAuthenticationResultsHeader (Options, format, encoding, Field, value); - case HeaderId.ArcMessageSignature: - case HeaderId.ArcSeal: - case HeaderId.DkimSignature: - return EncodeDkimOrArcSignatureHeader (Options, format, encoding, Field, value); - default: - return EncodeUnstructuredHeader (Options, format, encoding, Field, value); - } - } - - internal byte[] GetRawValue (FormatOptions format) - { - if (format.International && !explicitRawValue) { - if (textValue == null) - textValue = Unfold (Rfc2047.DecodeText (Options, rawValue)); - - // Note: if we're reformatting to be International, then charset doesn't matter. - return FormatRawValue (format, CharsetUtils.UTF8, textValue); - } - - return rawValue; - } - - /// - /// Sets the header value using the specified formatting options and character encoding. - /// - /// - /// When a particular charset is desired for encoding the header value - /// according to the rules of rfc2047, this method should be used - /// instead of the setter. - /// - /// The formatting options. - /// A character encoding. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public void SetValue (FormatOptions format, Encoding encoding, string value) - { - if (format == null) - throw new ArgumentNullException (nameof (format)); - - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - textValue = Unfold (value.Trim ()); - - rawValue = FormatRawValue (format, encoding, textValue); - - // cache the formatting options that change the way the header is formatted - //allowMixedHeaderCharsets = format.AllowMixedHeaderCharsets; - //newLineFormat = format.NewLineFormat; - //international = format.International; - //charset = encoding; - - OnChanged (); - } - - /// - /// Sets the header value using the specified character encoding. - /// - /// - /// When a particular charset is desired for encoding the header value - /// according to the rules of rfc2047, this method should be used - /// instead of the setter. - /// - /// A character encoding. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// - public void SetValue (Encoding encoding, string value) - { - SetValue (FormatOptions.Default, encoding, value); - } - - /// - /// Sets the header value using the specified formatting options and charset. - /// - /// - /// When a particular charset is desired for encoding the header value - /// according to the rules of rfc2047, this method should be used - /// instead of the setter. - /// - /// The formatting options. - /// A charset encoding. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is not supported. - /// - public void SetValue (FormatOptions format, string charset, string value) - { - if (format == null) - throw new ArgumentNullException (nameof (format)); - - if (charset == null) - throw new ArgumentNullException (nameof (charset)); - - var encoding = CharsetUtils.GetEncoding (charset); - - SetValue (format, encoding, value); - } - - /// - /// Sets the header value using the specified charset. - /// - /// - /// When a particular charset is desired for encoding the header value - /// according to the rules of rfc2047, this method should be used - /// instead of the setter. - /// - /// A charset encoding. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not supported. - /// - public void SetValue (string charset, string value) - { - if (charset == null) - throw new ArgumentNullException (nameof (charset)); - - var encoding = CharsetUtils.GetEncoding (charset); - - SetValue (FormatOptions.Default, encoding, value); - } - - /// - /// Set the raw header value. - /// - /// - /// Sets the raw header value. - /// This method can be used to override default encoding and folding behavior - /// for a particular header. - /// - /// The raw header value. - /// - /// is null. - /// - /// - /// does not end with a new-line character. - /// - public void SetRawValue (byte[] value) - { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (value.Length == 0 || value[value.Length - 1] != (byte) '\n') - throw new ArgumentException ("The raw value MUST end with a new-line character.", nameof (value)); - - explicitRawValue = true; - rawValue = value; - textValue = null; - - OnChanged (); - } - - internal event EventHandler Changed; - - void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - - /// - /// Returns a string representation of the header. - /// - /// - /// Formats the header field and value in a way that is suitable for display. - /// - /// A string representing the . - public override string ToString () - { - return IsInvalid ? Field : Field + ": " + Value; - } - - /// - /// Unfold the specified header value. - /// - /// - /// Unfolds the header value so that it becomes suitable for display. - /// Since is already unfolded, this method is really - /// only needed when working with raw header strings. - /// - /// The unfolded header value. - /// The header text. - public static unsafe string Unfold (string text) - { - int startIndex; - int endIndex; - int i = 0; - - if (text == null) - return string.Empty; - - while (i < text.Length && char.IsWhiteSpace (text[i])) - i++; - - if (i == text.Length) - return string.Empty; - - startIndex = i; - endIndex = i; - - while (i < text.Length) { - if (!char.IsWhiteSpace (text[i++])) - endIndex = i; - } - - int count = endIndex - startIndex; - char[] chars = new char[count]; - - fixed (char* outbuf = chars) { - char* outptr = outbuf; - - for (i = startIndex; i < endIndex; i++) { - if (text[i] != '\r' && text[i] != '\n') - *outptr++ = text[i]; - } - - count = (int) (outptr - outbuf); - } - - return new string (chars, 0, count); - } - - static bool IsAsciiAtom (byte c) - { - return c.IsAsciiAtom (); - } - - static bool IsControl (byte c) - { - return c.IsCtrl (); - } - - static bool IsBlank (byte c) - { - return c.IsBlank (); - } - - internal static unsafe bool TryParse (ParserOptions options, byte* input, int length, bool strict, out Header header) - { - byte* inend = input + length; - byte* start = input; - byte* inptr = input; - var invalid = false; - - // find the end of the field name - if (strict) { - while (inptr < inend && IsAsciiAtom (*inptr)) - inptr++; - } else { - while (inptr < inend && *inptr != (byte) ':' && !IsControl (*inptr)) - inptr++; - } - - while (inptr < inend && IsBlank (*inptr)) - inptr++; - - if (inptr == inend || *inptr != ':') { - if (strict) { - header = null; - return false; - } - - invalid = true; - inptr = inend; - } - - var field = new byte[(int) (inptr - start)]; - fixed (byte* outbuf = field) { - byte* outptr = outbuf; - - while (start < inptr) - *outptr++ = *start++; - } - - byte[] value; - - if (inptr < inend) { - inptr++; - - int count = (int) (inend - inptr); - value = new byte[count]; - - fixed (byte* outbuf = value) { - byte* outptr = outbuf; - - while (inptr < inend) - *outptr++ = *inptr++; - } - } else { - value = new byte[0]; - } - - header = new Header (options, field, value, invalid); - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a header from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true, if the header was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed header. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out Header header) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - unsafe { - fixed (byte* inptr = buffer) { - return TryParse (options.Clone (), inptr + startIndex, length, true, out header); - } - } - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a header from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true, if the header was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed header. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out Header header) - { - return TryParse (ParserOptions.Default, buffer, startIndex, length, out header); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a header from the supplied buffer starting at the specified index. - /// - /// true, if the header was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed header. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, out Header header) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int length = buffer.Length - startIndex; - - unsafe { - fixed (byte* inptr = buffer) { - return TryParse (options.Clone (), inptr + startIndex, length, true, out header); - } - } - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a header from the supplied buffer starting at the specified index. - /// - /// true, if the header was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed header. - /// - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (byte[] buffer, int startIndex, out Header header) - { - return TryParse (ParserOptions.Default, buffer, startIndex, out header); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a header from the specified buffer. - /// - /// true, if the header was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The parsed header. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, out Header header) - { - return TryParse (options, buffer, 0, out header); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a header from the specified buffer. - /// - /// true, if the header was successfully parsed, false otherwise. - /// The input buffer. - /// The parsed header. - /// - /// is null. - /// - public static bool TryParse (byte[] buffer, out Header header) - { - return TryParse (ParserOptions.Default, buffer, out header); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a header from the specified text. - /// - /// true, if the header was successfully parsed, false otherwise. - /// The parser options to use. - /// The text to parse. - /// The parsed header. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, string text, out Header header) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - - unsafe { - fixed (byte *inptr = buffer) { - return TryParse (options.Clone (), inptr, buffer.Length, true, out header); - } - } - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a header from the specified text. - /// - /// true, if the header was successfully parsed, false otherwise. - /// The text to parse. - /// The parsed header. - /// - /// is null. - /// - public static bool TryParse (string text, out Header header) - { - return TryParse (ParserOptions.Default, text, out header); - } - } -} diff --git a/src/MimeKit/HeaderId.cs b/src/MimeKit/HeaderId.cs deleted file mode 100644 index 33f8a8c..0000000 --- a/src/MimeKit/HeaderId.cs +++ /dev/null @@ -1,792 +0,0 @@ -// -// HeaderId.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Text; -using System.Reflection; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// An enumeration of common header fields. - /// - /// - /// Comparing enum values is not only faster, but less error prone than - /// comparing strings. - /// - public enum HeaderId { - /// - /// The Accept-Language header field. - /// - AcceptLanguage, - - /// - /// The Ad-Hoc header field. - /// - AdHoc, - - /// - /// The Alternate-Recipient header field. - /// - AlternateRecipient, - - /// - /// The Apparently-To header field. - /// - ApparentlyTo, - - /// - /// The Approved header field. - /// - Approved, - - /// - /// The ARC-Authentication-Results header field. - /// - [HeaderName ("ARC-Authentication-Results")] - ArcAuthenticationResults, - - /// - /// The ARC-Message-Signature header field. - /// - [HeaderName ("ARC-Message-Signature")] - ArcMessageSignature, - - /// - /// The ARC-Seal header field. - /// - [HeaderName ("ARC-Seal")] - ArcSeal, - - /// - /// The Archive header field. - /// - Archive, - - /// - /// The Archived-At header field. - /// - ArchivedAt, - - /// - /// The Article header field. - /// - Article, - - /// - /// The Authentication-Results header field. - /// - AuthenticationResults, - - /// - /// The Autocrypt header field. - /// - Autocrypt, - - /// - /// The Autocrypt-Gossip header field. - /// - AutocryptGossip, - - /// - /// The Autocrypt-Setup-Message header field. - /// - AutocryptSetupMessage, - - /// - /// The Autoforwarded header field. - /// - Autoforwarded, - - /// - /// The Auto-Submitted header field. - /// - AutoSubmitted, - - /// - /// The Autosubmitted header field. - /// - Autosubmitted, - - /// - /// The Base header field. - /// - Base, - - /// - /// The Bcc header field. - /// - Bcc, - - /// - /// The Body header field. - /// - Body, - - /// - /// The Bytes header field. - /// - Bytes, - - /// - /// The Cc header field. - /// - Cc, - - /// - /// The Comments header field. - /// - Comments, - - /// - /// The Content-Alternative header field. - /// - ContentAlternative, - - /// - /// The Content-Base header field. - /// - ContentBase, - - /// - /// The Content-Class header field. - /// - ContentClass, - - /// - /// The Content-Description header field. - /// - ContentDescription, - - /// - /// The Content-Disposition header field. - /// - ContentDisposition, - - /// - /// The Content-Duration header field. - /// - ContentDuration, - - /// - /// The Content-Features header field. - /// - ContentFeatures, - - /// - /// The Content-Id header field. - /// - ContentId, - - /// - /// The Content-Identifier header field. - /// - ContentIdentifier, - - /// - /// The Content-Language header field. - /// - ContentLanguage, - - /// - /// The Content-Length header field. - /// - ContentLength, - - /// - /// The Content-Location header field. - /// - ContentLocation, - - /// - /// The Content-Md5 header field. - /// - ContentMd5, - - /// - /// The Content-Return header field. - /// - ContentReturn, - - /// - /// The Content-Transfer-Encoding header field. - /// - ContentTransferEncoding, - - /// - /// The Content-Translation-Type header field. - /// - ContentTranslationType, - - /// - /// The Content-Type header field. - /// - ContentType, - - /// - /// The Control header field. - /// - Control, - - /// - /// The Conversion header field. - /// - Conversion, - - /// - /// The Conversion-With-Loss header field. - /// - ConversionWithLoss, - - /// - /// The Date header field. - /// - Date, - - /// - /// The Date-Received header field. - /// - DateReceived, - - /// - /// The Deferred-Delivery header field. - /// - DeferredDelivery, - - /// - /// The Delivery-Date header field. - /// - DeliveryDate, - - /// - /// The Disclose-Recipients header field. - /// - DiscloseRecipients, - - /// - /// The Disposition-Notification-Options header field. - /// - DispositionNotificationOptions, - - /// - /// The Disposition-Notification-To header field. - /// - DispositionNotificationTo, - - /// - /// The Distribution header field. - /// - Distribution, - - /// - /// The DKIM-Signature header field. - /// - [HeaderName ("DKIM-Signature")] - DkimSignature, - - /// - /// The DomainKey-Signature header field. - /// - [HeaderName ("DomainKey-Signature")] - DomainKeySignature, - - /// - /// The Encoding header field. - /// - Encoding, - - /// - /// The Encrypted header field. - /// - Encrypted, - - /// - /// The Expires header field. - /// - Expires, - - /// - /// The Expiry-Date header field. - /// - ExpiryDate, - - /// - /// The Followup-To header field. - /// - FollowupTo, - - /// - /// The From header field. - /// - From, - - /// - /// The Generate-Delivery-Report header field. - /// - GenerateDeliveryReport, - - /// - /// The Importance header field. - /// - Importance, - - /// - /// The Injection-Date header field. - /// - InjectionDate, - - /// - /// The Injection-Info header field. - /// - InjectionInfo, - - /// - /// The In-Reply-To header field. - /// - InReplyTo, - - /// - /// The Keywords header field. - /// - Keywords, - - /// - /// The Language header. - /// - Language, - - /// - /// The Latest-Delivery-Time header. - /// - LatestDeliveryTime, - - /// - /// The Lines header field. - /// - Lines, - - /// - /// THe List-Archive header field. - /// - ListArchive, - - /// - /// The List-Help header field. - /// - ListHelp, - - /// - /// The List-Id header field. - /// - ListId, - - /// - /// The List-Owner header field. - /// - ListOwner, - - /// - /// The List-Post header field. - /// - ListPost, - - /// - /// The List-Subscribe header field. - /// - ListSubscribe, - - /// - /// The List-Unsubscribe header field. - /// - ListUnsubscribe, - - /// - /// The List-Unsubscribe-Post header field. - /// - ListUnsubscribePost, - - /// - /// The Message-Id header field. - /// - MessageId, - - /// - /// The MIME-Version header field. - /// - [HeaderName ("MIME-Version")] - MimeVersion, - - /// - /// The Newsgroups header field. - /// - Newsgroups, - - /// - /// The Nntp-Posting-Host header field. - /// - NntpPostingHost, - - /// - /// The Organization header field. - /// - Organization, - - /// - /// The Original-From header field. - /// - OriginalFrom, - - /// - /// The Original-Message-Id header field. - /// - OriginalMessageId, - - /// - /// The Original-Recipient header field. - /// - OriginalRecipient, - - /// - /// The Original-Return-Address header field. - /// - OriginalReturnAddress, - - /// - /// The Original-Subject header field. - /// - OriginalSubject, - - /// - /// The Path header field. - /// - Path, - - /// - /// The Precedence header field. - /// - Precedence, - - /// - /// The Prevent-NonDelivery-Report header field. - /// - [HeaderName ("Prevent-NonDelivery-Report")] - PreventNonDeliveryReport, - - /// - /// The Priority header field. - /// - Priority, - - /// - /// The Received header field. - /// - Received, - - /// - /// The Received-SPF header field. - /// - [HeaderName ("Received-SPF")] - ReceivedSPF, - - /// - /// The References header field. - /// - References, - - /// - /// The Relay-Version header field. - /// - RelayVersion, - - /// - /// The Reply-By header field. - /// - ReplyBy, - - /// - /// The Reply-To header field. - /// - ReplyTo, - - /// - /// The Require-Recipient-Valid-Since header field. - /// - RequireRecipientValidSince, - - /// - /// The Resent-Bcc header field. - /// - ResentBcc, - - /// - /// The Resent-Cc header field. - /// - ResentCc, - - /// - /// The Resent-Date header field. - /// - ResentDate, - - /// - /// The Resent-From header field. - /// - ResentFrom, - - /// - /// The Resent-Message-Id header field. - /// - ResentMessageId, - - /// - /// The Resent-Reply-To header field. - /// - ResentReplyTo, - - /// - /// The Resent-Sender header field. - /// - ResentSender, - - /// - /// The Resent-To header field. - /// - ResentTo, - - /// - /// The Return-Path header field. - /// - ReturnPath, - - /// - /// The Return-Receipt-To header field. - /// - ReturnReceiptTo, - - /// - /// The See-Also header field. - /// - SeeAlso, - - /// - /// The Sender header field. - /// - Sender, - - /// - /// The Sensitivity header field. - /// - Sensitivity, - - /// - /// The Solicitation header field. - /// - Solicitation, - - /// - /// The Status header field. - /// - Status, - - /// - /// The Subject header field. - /// - Subject, - - /// - /// The Summary header field. - /// - Summary, - - /// - /// The Supersedes header field. - /// - Supersedes, - - /// - /// The To header field. - /// - To, - - /// - /// The User-Agent header field. - /// - UserAgent, - - /// - /// The X400-Content-Identifier header field. - /// - [HeaderName ("X400-Content-Identifier")] - X400ContentIdentifier, - - /// - /// The X400-Content-Return header field. - /// - [HeaderName ("X400-Content-Return")] - X400ContentReturn, - - /// - /// The X400-Content-Type header field. - /// - [HeaderName ("X400-Content-Type")] - X400ContentType, - - /// - /// The X400-MTS-Identifier header field. - /// - [HeaderName ("X400-MTS-Identifier")] - X400MTSIdentifier, - - /// - /// The X400-Originator header field. - /// - [HeaderName ("X400-Originator")] - X400Originator, - - /// - /// The X400-Received header field. - /// - [HeaderName ("X400-Received")] - X400Received, - - /// - /// The X400-Recipients header field. - /// - [HeaderName ("X400-Recipients")] - X400Recipients, - - /// - /// The X400-Trace header field. - /// - [HeaderName ("X400-Trace")] - X400Trace, - - /// - /// The X-Mailer header field. - /// - XMailer, - - /// - /// The X-MSMail-Priority header field. - /// - [HeaderName ("X-MSMail-Priority")] - XMSMailPriority, - - /// - /// The X-Priority header field. - /// - XPriority, - - /// - /// The X-Status header field. - /// - XStatus, - - /// - /// An unknown header field. - /// - Unknown = -1 - } - - [AttributeUsage (AttributeTargets.Field)] - class HeaderNameAttribute : Attribute { - public HeaderNameAttribute (string name) - { - HeaderName = name; - } - - public string HeaderName { - get; protected set; - } - } - - /// - /// extension methods. - /// - /// - /// extension methods. - /// - public static class HeaderIdExtensions - { - static readonly Dictionary dict; - - static HeaderIdExtensions () - { - var values = (HeaderId[]) Enum.GetValues (typeof (HeaderId)); - - dict = new Dictionary (values.Length - 1, MimeUtils.OrdinalIgnoreCase); - - for (int i = 0; i < values.Length - 1; i++) - dict.Add (values[i].ToHeaderName (), values[i]); - } - - /// - /// Converts the enum value into the equivalent header field name. - /// - /// - /// Converts the enum value into the equivalent header field name. - /// - /// The header name. - /// The enum value. - public static string ToHeaderName (this HeaderId value) - { - var name = value.ToString (); - -#if NETSTANDARD1_3 || NETSTANDARD1_6 - var field = typeof (HeaderId).GetTypeInfo ().GetDeclaredField (name); - var attrs = field.GetCustomAttributes (typeof (HeaderNameAttribute), false).ToArray (); -#else - var field = typeof (HeaderId).GetField (name); - var attrs = field.GetCustomAttributes (typeof (HeaderNameAttribute), false); -#endif - - if (attrs != null && attrs.Length == 1) - return ((HeaderNameAttribute) attrs[0]).HeaderName; - - var builder = new StringBuilder (name); - - for (int i = 1; i < builder.Length; i++) { - if (char.IsUpper (builder[i])) - builder.Insert (i++, '-'); - } - - return builder.ToString (); - } - - internal static HeaderId ToHeaderId (this string name) - { - HeaderId value; - - if (!dict.TryGetValue (name, out value)) - return HeaderId.Unknown; - - return value; - } - } -} diff --git a/src/MimeKit/HeaderList.cs b/src/MimeKit/HeaderList.cs deleted file mode 100644 index 0317d41..0000000 --- a/src/MimeKit/HeaderList.cs +++ /dev/null @@ -1,1547 +0,0 @@ -// -// HeaderList.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Collections; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit.IO; -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A list of s. - /// - /// - /// Represents a list of headers as found in a - /// or . - /// - public sealed class HeaderList : IList
- { - internal readonly ParserOptions Options; - - // this table references the first header of each field - readonly Dictionary table; - readonly List
headers; - - internal HeaderList (ParserOptions options) - { - table = new Dictionary (MimeUtils.OrdinalIgnoreCase); - headers = new List
(); - Options = options; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new empty header list. - /// - public HeaderList () : this (ParserOptions.Default.Clone ()) - { - } - - /// - /// Add a header with the specified field and value. - /// - /// - /// Adds a new header for the specified field and value pair. - /// - /// The header identifier. - /// The header value. - /// - /// is null. - /// - /// - /// is not a valid . - /// - public void Add (HeaderId id, string value) - { - Add (id, Encoding.UTF8, value); - } - - /// - /// Add a header with the specified field and value. - /// - /// - /// Adds a new header for the specified field and value pair. - /// - /// The name of the header field. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - public void Add (string field, string value) - { - Add (field, Encoding.UTF8, value); - } - - /// - /// Add a header with the specified field and value. - /// - /// - /// Adds a new header for the specified field and value pair. - /// - /// The header identifier. - /// The character encoding to use for the value. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid . - /// - public void Add (HeaderId id, Encoding encoding, string value) - { - Add (new Header (encoding, id, value)); - } - - /// - /// Add a header with the specified field and value. - /// - /// - /// Adds a new header for the specified field and value pair. - /// - /// The name of the header field. - /// The character encoding to use for the value. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - public void Add (string field, Encoding encoding, string value) - { - Add (new Header (encoding, field, value)); - } - - /// - /// Check if the contains a header with the specified field name. - /// - /// - /// Determines whether or not the header list contains the specified header. - /// - /// true if the requested header exists; - /// otherwise false. - /// The header identifier. - /// - /// is not a valid . - /// - public bool Contains (HeaderId id) - { - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - return table.ContainsKey (id.ToHeaderName ()); - } - - /// - /// Check if the contains a header with the specified field name. - /// - /// - /// Determines whether or not the header list contains the specified header. - /// - /// true if the requested header exists; - /// otherwise false. - /// The name of the header field. - /// - /// is null. - /// - public bool Contains (string field) - { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - return table.ContainsKey (field); - } - - /// - /// Get the index of the requested header, if it exists. - /// - /// - /// Finds the first index of the specified header, if it exists. - /// - /// The index of the requested header; otherwise -1. - /// The header id. - /// - /// is not a valid . - /// - public int IndexOf (HeaderId id) - { - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - for (int i = 0; i < headers.Count; i++) { - if (headers[i].Id == id) - return i; - } - - return -1; - } - - /// - /// Get the index of the requested header, if it exists. - /// - /// - /// Finds the first index of the specified header, if it exists. - /// - /// The index of the requested header; otherwise -1. - /// The name of the header field. - /// - /// is null. - /// - public int IndexOf (string field) - { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - for (int i = 0; i < headers.Count; i++) { - if (headers[i].Field.Equals (field, StringComparison.OrdinalIgnoreCase)) - return i; - } - - return -1; - } - - /// - /// Insert a header with the specified field and value at the given index. - /// - /// - /// Inserts the header at the specified index in the list. - /// - /// The index to insert the header. - /// The header identifier. - /// The header value. - /// - /// is null. - /// - /// - /// is not a valid . - /// -or- - /// is out of range. - /// - public void Insert (int index, HeaderId id, string value) - { - Insert (index, id, Encoding.UTF8, value); - } - - /// - /// Insert a header with the specified field and value at the given index. - /// - /// - /// Inserts the header at the specified index in the list. - /// - /// The index to insert the header. - /// The name of the header field. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - /// - /// is out of range. - /// - public void Insert (int index, string field, string value) - { - Insert (index, field, Encoding.UTF8, value); - } - - /// - /// Insert a header with the specified field and value at the given index. - /// - /// - /// Inserts the header at the specified index in the list. - /// - /// The index to insert the header. - /// The header identifier. - /// The character encoding to use for the value. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid . - /// -or- - /// is out of range. - /// - public void Insert (int index, HeaderId id, Encoding encoding, string value) - { - Insert (index, new Header (encoding, id, value)); - } - - /// - /// Insert a header with the specified field and value at the given index. - /// - /// - /// Inserts the header at the specified index in the list. - /// - /// The index to insert the header. - /// The name of the header field. - /// The character encoding to use for the value. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - /// - /// is out of range. - /// - public void Insert (int index, string field, Encoding encoding, string value) - { - Insert (index, new Header (encoding, field, value)); - } - - /// - /// Get the last index of the requested header, if it exists. - /// - /// - /// Finds the last index of the specified header, if it exists. - /// - /// The last index of the requested header; otherwise -1. - /// The header id. - /// - /// is not a valid . - /// - public int LastIndexOf (HeaderId id) - { - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - for (int i = headers.Count - 1; i >= 0; i--) { - if (headers[i].Id == id) - return i; - } - - return -1; - } - - /// - /// Get the last index of the requested header, if it exists. - /// - /// - /// Finds the last index of the specified header, if it exists. - /// - /// The last index of the requested header; otherwise -1. - /// The name of the header field. - /// - /// is null. - /// - public int LastIndexOf (string field) - { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - for (int i = headers.Count - 1; i >= 0; i--) { - if (headers[i].Field.Equals (field, StringComparison.OrdinalIgnoreCase)) - return i; - } - - return -1; - } - - /// - /// Remove the first occurance of the specified header field. - /// - /// - /// Removes the first occurance of the specified header field, if any exist. - /// - /// true if the first occurance of the specified - /// header was removed; otherwise false. - /// The header identifier. - /// - /// is is not a valid . - /// - public bool Remove (HeaderId id) - { - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - Header header; - if (!table.TryGetValue (id.ToHeaderName (), out header)) - return false; - - return Remove (header); - } - - /// - /// Remove the first occurance of the specified header field. - /// - /// - /// Removes the first occurance of the specified header field, if any exist. - /// - /// true if the first occurance of the specified - /// header was removed; otherwise false. - /// The name of the header field. - /// - /// is null. - /// - public bool Remove (string field) - { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - Header header; - if (!table.TryGetValue (field, out header)) - return false; - - return Remove (header); - } - - /// - /// Remove all of the headers matching the specified field name. - /// - /// - /// Removes all of the headers matching the specified field name. - /// - /// The header identifier. - /// - /// is not a valid . - /// - public void RemoveAll (HeaderId id) - { - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - table.Remove (id.ToHeaderName ()); - - for (int i = headers.Count - 1; i >= 0; i--) { - if (headers[i].Id != id) - continue; - - var header = headers[i]; - headers.RemoveAt (i); - - OnChanged (header, HeaderListChangedAction.Removed); - } - } - - /// - /// Remove all of the headers matching the specified field name. - /// - /// - /// Removes all of the headers matching the specified field name. - /// - /// The name of the header field. - /// - /// is null. - /// - public void RemoveAll (string field) - { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - table.Remove (field); - - for (int i = headers.Count - 1; i >= 0; i--) { - if (!headers[i].Field.Equals (field, StringComparison.OrdinalIgnoreCase)) - continue; - - var header = headers[i]; - headers.RemoveAt (i); - - OnChanged (header, HeaderListChangedAction.Removed); - } - } - - /// - /// Replace all headers with identical field names with the single specified header. - /// - /// - /// Replaces all headers with identical field names with the single specified header. - /// If no headers with the specified field name exist, it is simply added. - /// - /// The header identifier. - /// The character encoding to use for the value. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a valid . - /// - public void Replace (HeaderId id, Encoding encoding, string value) - { - Replace (new Header (encoding, id, value)); - } - - /// - /// Replace all headers with identical field names with the single specified header. - /// - /// - /// Replaces all headers with identical field names with the single specified header. - /// If no headers with the specified field name exist, it is simply added. - /// - /// The header identifier. - /// The header value. - /// - /// is null. - /// - /// - /// is not a valid . - /// - public void Replace (HeaderId id, string value) - { - Replace (new Header (id, value)); - } - - /// - /// Replace all headers with identical field names with the single specified header. - /// - /// - /// Replaces all headers with identical field names with the single specified header. - /// If no headers with the specified field name exist, it is simply added. - /// - /// The name of the header field. - /// The character encoding to use for the value. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - public void Replace (string field, Encoding encoding, string value) - { - Replace (new Header (encoding, field, value)); - } - - /// - /// Replace all headers with identical field names with the single specified header. - /// - /// - /// Replaces all headers with identical field names with the single specified header. - /// If no headers with the specified field name exist, it is simply added. - /// - /// The name of the header field. - /// The header value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - public void Replace (string field, string value) - { - Replace (new Header (field, value)); - } - - /// - /// Get or set the value of the first occurance of a header - /// with the specified field name. - /// - /// - /// Gets or sets the value of the first occurance of a header - /// with the specified field name. - /// - /// The value of the first occurrance of the specified header if it exists; otherwise null. - /// The header identifier. - /// - /// is null. - /// - public string this [HeaderId id] { - get { - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - Header header; - if (table.TryGetValue (id.ToHeaderName (), out header)) - return header.Value; - - return null; - } - set { - if (id == HeaderId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Header header; - if (table.TryGetValue (id.ToHeaderName (), out header)) { - header.Value = value; - } else { - Add (id, value); - } - } - } - - /// - /// Get or set the value of the first occurance of a header - /// with the specified field name. - /// - /// - /// Gets or sets the value of the first occurance of a header - /// with the specified field name. - /// - /// The value of the first occurrance of the specified header if it exists; otherwise null. - /// The name of the header field. - /// - /// is null. - /// -or- - /// is null. - /// - public string this [string field] { - get { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - Header header; - if (table.TryGetValue (field, out header)) - return header.Value; - - return null; - } - set { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Header header; - if (table.TryGetValue (field, out header)) { - header.Value = value; - } else { - Add (field, value); - } - } - } - - /// - /// Write the to the specified output stream. - /// - /// - /// Writes all of the headers to the output stream. - /// - /// The formatting options. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - foreach (var header in headers) { - filtered.Write (header.RawField, 0, header.RawField.Length, cancellationToken); - - if (!header.IsInvalid) { - var rawValue = header.GetRawValue (options); - - filtered.Write (Header.Colon, 0, Header.Colon.Length, cancellationToken); - filtered.Write (rawValue, 0, rawValue.Length, cancellationToken); - } - } - - filtered.Flush (cancellationToken); - } - - var cancellable = stream as ICancellableStream; - - if (cancellable != null) { - cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - } - - /// - /// Asynchronously write the to the specified output stream. - /// - /// - /// Writes all of the headers to the output stream. - /// - /// A task that represents the asynchronous write operation. - /// The formatting options. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public async Task WriteToAsync (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - foreach (var header in headers) { - await filtered.WriteAsync (header.RawField, 0, header.RawField.Length, cancellationToken).ConfigureAwait (false); - - if (!header.IsInvalid) { - var rawValue = header.GetRawValue (options); - - await filtered.WriteAsync (Header.Colon, 0, Header.Colon.Length, cancellationToken).ConfigureAwait (false); - await filtered.WriteAsync (rawValue, 0, rawValue.Length, cancellationToken).ConfigureAwait (false); - } - } - - await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); - } - - await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); - } - - /// - /// Write the to the specified output stream. - /// - /// - /// Writes all of the headers to the output stream. - /// - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (FormatOptions.Default, stream, cancellationToken); - } - - /// - /// Asynchronously write the to the specified output stream. - /// - /// - /// Writes all of the headers to the output stream. - /// - /// A task that represents the asynchronous write operation. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (FormatOptions.Default, stream, cancellationToken); - } - - #region ICollection implementation - - /// - /// Get the number of headers in the list. - /// - /// - /// Gets the number of headers in the list. - /// - /// The number of headers. - public int Count { - get { return headers.Count; } - } - - /// - /// Get whether or not the header list is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Add the specified header. - /// - /// - /// Adds the specified header to the end of the header list. - /// - /// The header to add. - /// - /// is null. - /// - public void Add (Header header) - { - if (header == null) - throw new ArgumentNullException (nameof (header)); - - if (!table.ContainsKey (header.Field)) - table.Add (header.Field, header); - - header.Changed += HeaderChanged; - headers.Add (header); - - OnChanged (header, HeaderListChangedAction.Added); - } - - /// - /// Clear the header list. - /// - /// - /// Removes all of the headers from the list. - /// - public void Clear () - { - foreach (var header in headers) - header.Changed -= HeaderChanged; - - headers.Clear (); - table.Clear (); - - OnChanged (null, HeaderListChangedAction.Cleared); - } - - /// - /// Check if the contains the specified header. - /// - /// - /// Determines whether or not the header list contains the specified header. - /// - /// true if the specified header is contained; - /// otherwise, false. - /// The header. - /// - /// is null. - /// - public bool Contains (Header header) - { - if (header == null) - throw new ArgumentNullException (nameof (header)); - - return headers.Contains (header); - } - - /// - /// Copy all of the headers in the to the specified array. - /// - /// - /// Copies all of the headers within the into the array, - /// starting at the specified array index. - /// - /// The array to copy the headers to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (Header[] array, int arrayIndex) - { - headers.CopyTo (array, arrayIndex); - } - - /// - /// Remove the specified header. - /// - /// - /// Removes the specified header from the list if it exists. - /// - /// true if the specified header was removed; - /// otherwise false. - /// The header. - /// - /// is null. - /// - public bool Remove (Header header) - { - if (header == null) - throw new ArgumentNullException (nameof (header)); - - int index = headers.IndexOf (header); - - if (index == -1) - return false; - - header.Changed -= HeaderChanged; - - if (table[header.Field] == header) { - table.Remove (header.Field); - - // find the next matching header and add it to the lookup table - for (int i = index + 1; i < headers.Count; i++) { - if (headers[i].Field.Equals (header.Field, StringComparison.OrdinalIgnoreCase)) { - table.Add (headers[i].Field, headers[i]); - break; - } - } - } - - headers.RemoveAt (index); - - OnChanged (header, HeaderListChangedAction.Removed); - - return true; - } - - /// - /// Replace all headers with identical field names with the single specified header. - /// - /// - /// Replaces all headers with identical field names with the single specified header. - /// If no headers with the specified field name exist, it is simply added. - /// - /// The header. - /// - /// is null. - /// - public void Replace (Header header) - { - int i; - - if (header == null) - throw new ArgumentNullException (nameof (header)); - - Header first; - if (!table.TryGetValue (header.Field, out first)) { - Add (header); - return; - } - - for (i = headers.Count - 1; i >= 0; i--) { - if (headers[i] == first) - break; - - if (!headers[i].Field.Equals (header.Field, StringComparison.OrdinalIgnoreCase)) - continue; - - headers[i].Changed -= HeaderChanged; - headers.RemoveAt (i); - } - - header.Changed += HeaderChanged; - first.Changed -= HeaderChanged; - - table[header.Field] = header; - headers[i] = header; - - OnChanged (first, HeaderListChangedAction.Removed); - OnChanged (header, HeaderListChangedAction.Added); - } - - #endregion - - #region IList implementation - - /// - /// Get the index of the requested header, if it exists. - /// - /// - /// Finds the index of the specified header, if it exists. - /// - /// The index of the requested header; otherwise -1. - /// The header. - /// - /// is null. - /// - public int IndexOf (Header header) - { - if (header == null) - throw new ArgumentNullException (nameof (header)); - - return headers.IndexOf (header); - } - - /// - /// Insert the specified header at the given index. - /// - /// - /// Inserts the header at the specified index in the list. - /// - /// The index to insert the header. - /// The header. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void Insert (int index, Header header) - { - if (index < 0 || index > Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (header == null) - throw new ArgumentNullException (nameof (header)); - - // update the lookup table - Header hdr; - if (table.TryGetValue (header.Field, out hdr)) { - int idx = headers.IndexOf (hdr); - - if (idx >= index) - table[header.Field] = header; - } else { - table.Add (header.Field, header); - } - - headers.Insert (index, header); - header.Changed += HeaderChanged; - - OnChanged (header, HeaderListChangedAction.Added); - } - - /// - /// Remove the header at the specified index. - /// - /// - /// Removes the header at the specified index. - /// - /// The index. - /// - /// is out of range. - /// - public void RemoveAt (int index) - { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - var header = headers[index]; - - header.Changed -= HeaderChanged; - - if (table[header.Field] == header) { - table.Remove (header.Field); - - // find the next matching header and add it to the lookup table - for (int i = index + 1; i < headers.Count; i++) { - if (headers[i].Field.Equals (header.Field, StringComparison.OrdinalIgnoreCase)) { - table.Add (headers[i].Field, headers[i]); - break; - } - } - } - - headers.RemoveAt (index); - - OnChanged (header, HeaderListChangedAction.Removed); - } - - /// - /// Get or set the at the specified index. - /// - /// - /// Gets or sets the at the specified index. - /// - /// The header at the specified index. - /// The index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public Header this [int index] { - get { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - return headers[index]; - } - set { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - var header = headers[index]; - - if (header == value) - return; - - header.Changed -= HeaderChanged; - value.Changed += HeaderChanged; - - if (header.Field.Equals (value.Field, StringComparison.OrdinalIgnoreCase)) { - // replace the old header with the new one - if (table[header.Field] == header) - table[header.Field] = value; - } else { - // update the table for the header field being replaced - if (table[header.Field] == header) { - table.Remove (header.Field); - - // find the next matching header and add it to the lookup table - for (int i = index + 1; i < headers.Count; i++) { - if (headers[i].Field.Equals (header.Field, StringComparison.OrdinalIgnoreCase)) { - table.Add (headers[i].Field, headers[i]); - break; - } - } - } - - // update the table for the header being set - if (table.TryGetValue (value.Field, out header)) { - int idx = headers.IndexOf (header); - - if (idx > index) - table[header.Field] = value; - } else { - table.Add (value.Field, value); - } - } - - headers[index] = value; - - if (header.Field.Equals (value.Field, StringComparison.OrdinalIgnoreCase)) { - OnChanged (value, HeaderListChangedAction.Changed); - } else { - OnChanged (header, HeaderListChangedAction.Removed); - OnChanged (value, HeaderListChangedAction.Added); - } - } - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the list of headers. - /// - /// - /// Gets an enumerator for the list of headers. - /// - /// The enumerator. - public IEnumerator
GetEnumerator () - { - return headers.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the list of headers. - /// - /// - /// Gets an enumerator for the list of headers. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return headers.GetEnumerator (); - } - - #endregion - - internal event EventHandler Changed; - - void HeaderChanged (object sender, EventArgs args) - { - OnChanged ((Header) sender, HeaderListChangedAction.Changed); - } - - void OnChanged (Header header, HeaderListChangedAction action) - { - if (Changed != null) - Changed (this, new HeaderListChangedEventArgs (header, action)); - } - - internal bool TryGetHeader (string field, out Header header) - { - if (field == null) - throw new ArgumentNullException (nameof (field)); - - return table.TryGetValue (field, out header); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// - /// The parsed list of headers. - /// The parser options. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the headers. - /// - /// - /// An I/O error occurred. - /// - public static HeaderList Load (ParserOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new MimeParser (options, stream, MimeFormat.Entity); - - return parser.ParseHeaders (cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// - /// The parsed list of headers. - /// The parser options. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the headers. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (ParserOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new MimeParser (options, stream, MimeFormat.Entity); - - return parser.ParseHeadersAsync (cancellationToken); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// - /// The parsed list of headers. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static HeaderList Load (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, stream, cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// - /// The parsed list of headers. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, stream, cancellationToken); - } - - /// - /// Load a from the specified file. - /// - /// - /// Loads a from the file at the give file path, - /// using the specified . - /// - /// The parsed list of headers. - /// The parser options. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the headers. - /// - /// - /// An I/O error occurred. - /// - public static HeaderList Load (ParserOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - return Load (options, stream, cancellationToken); - } - - /// - /// Asynchronously load a from the specified file. - /// - /// - /// Loads a from the file at the give file path, - /// using the specified . - /// - /// The parsed list of headers. - /// The parser options. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the headers. - /// - /// - /// An I/O error occurred. - /// - public static async Task LoadAsync (ParserOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - return await LoadAsync (options, stream, cancellationToken).ConfigureAwait (false); - } - - /// - /// Load a from the specified file. - /// - /// - /// Loads a from the file at the give file path, - /// using the default . - /// - /// The parsed list of headers. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the headers. - /// - /// - /// An I/O error occurred. - /// - public static HeaderList Load (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, fileName, cancellationToken); - } - - /// - /// Asynchronously load a from the specified file. - /// - /// - /// Loads a from the file at the give file path, - /// using the default . - /// - /// The parsed list of headers. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the headers. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, fileName, cancellationToken); - } - } -} diff --git a/src/MimeKit/HeaderListChangedEventArgs.cs b/src/MimeKit/HeaderListChangedEventArgs.cs deleted file mode 100644 index 7da76af..0000000 --- a/src/MimeKit/HeaderListChangedEventArgs.cs +++ /dev/null @@ -1,74 +0,0 @@ -// -// HeaderChangedEventArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit { - /// - /// Header list changed action. - /// - /// - /// Specifies the way that a was changed. - /// - public enum HeaderListChangedAction { - /// - /// A header was added. - /// - Added, - - /// - /// A header was changed. - /// - Changed, - - /// - /// A header was removed. - /// - Removed, - - /// - /// The header list was cleared. - /// - Cleared - } - - class HeaderListChangedEventArgs : EventArgs - { - internal HeaderListChangedEventArgs (Header header, HeaderListChangedAction action) - { - Header = header; - Action = action; - } - - public HeaderListChangedAction Action { - get; private set; - } - - public Header Header { - get; private set; - } - } -} diff --git a/src/MimeKit/HeaderListCollection.cs b/src/MimeKit/HeaderListCollection.cs deleted file mode 100644 index 211e113..0000000 --- a/src/MimeKit/HeaderListCollection.cs +++ /dev/null @@ -1,254 +0,0 @@ -// -// HeaderListCollection.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace MimeKit { - /// - /// A collection of groups. - /// - /// - /// A collection of groups used with - /// . - /// - /// - public class HeaderListCollection : ICollection - { - readonly List groups; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public HeaderListCollection () - { - groups = new List (); - } - - /// - /// Gets the number of groups in the collection. - /// - /// - /// Gets the number of groups in the collection. - /// - /// The number of groups. - public int Count { - get { return groups.Count; } - } - - /// - /// Gets whether or not the header list collection is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Gets or sets the at the specified index. - /// - /// - /// Gets or sets the at the specified index. - /// - /// The group of headers at the specified index. - /// The index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public HeaderList this [int index] { - get { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - return groups[index]; - } - set { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (groups[index] == value) - return; - - groups[index].Changed -= OnGroupChanged; - value.Changed += OnGroupChanged; - groups[index] = value; - } - } - - /// - /// Adds the group of headers to the collection. - /// - /// - /// Adds the group of headers to the collection. - /// - /// The group of headers. - /// - /// is null. - /// - public void Add (HeaderList group) - { - if (group == null) - throw new ArgumentNullException (nameof (@group)); - - group.Changed += OnGroupChanged; - groups.Add (group); - OnChanged (); - } - - /// - /// Clears the header list collection. - /// - /// - /// Removes all of the groups from the collection. - /// - public void Clear () - { - for (int i = 0; i < groups.Count; i++) - groups[i].Changed -= OnGroupChanged; - - groups.Clear (); - OnChanged (); - } - - /// - /// Checks if the collection contains the specified group of headers. - /// - /// - /// Determines whether or not the collection contains the specified group of headers. - /// - /// true if the specified group of headers is contained; - /// otherwise, false. - /// The group of headers. - /// - /// is null. - /// - public bool Contains (HeaderList group) - { - if (group == null) - throw new ArgumentNullException (nameof (@group)); - - return groups.Contains (group); - } - - /// - /// Copies all of the header groups in the to the specified array. - /// - /// - /// Copies all of the header groups within the into the array, - /// starting at the specified array index. - /// - /// The array to copy the headers to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (HeaderList[] array, int arrayIndex) - { - groups.CopyTo (array, arrayIndex); - } - - /// - /// Removes the specified header group. - /// - /// - /// Removes the specified header group from the collection, if it exists. - /// - /// true if the specified header group was removed; - /// otherwise false. - /// The group of headers. - /// - /// is null. - /// - public bool Remove (HeaderList group) - { - if (group == null) - throw new ArgumentNullException (nameof (@group)); - - if (!groups.Remove (group)) - return false; - - group.Changed -= OnGroupChanged; - OnChanged (); - - return true; - } - - /// - /// Gets an enumerator for the groups of headers. - /// - /// - /// Gets an enumerator for the groups of headers. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return groups.GetEnumerator (); - } - - /// - /// Gets an enumerator for the groups of headers. - /// - /// - /// Gets an enumerator for the groups of headers. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - internal event EventHandler Changed; - - void OnChanged () - { - var handler = Changed; - - if (handler != null) - handler (this, EventArgs.Empty); - } - - void OnGroupChanged (object sender, HeaderListChangedEventArgs e) - { - OnChanged (); - } - } -} diff --git a/src/MimeKit/IMimeContent.cs b/src/MimeKit/IMimeContent.cs deleted file mode 100644 index d14331c..0000000 --- a/src/MimeKit/IMimeContent.cs +++ /dev/null @@ -1,178 +0,0 @@ -// -// IMimeContent.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MimeKit { - /// - /// An interface for content stream encapsulation as used by . - /// - /// - /// Implemented by . - /// - /// - /// - /// - public interface IMimeContent - { - /// - /// Get the content encoding. - /// - /// - /// If the is not encoded, this value will be - /// . Otherwise, it will be - /// set to the raw content encoding of the stream. - /// - /// The encoding. - ContentEncoding Encoding { get; } - - /// - /// Get the new-line format, if known. - /// - /// - /// This property is typically only set by the as it parses - /// the content of a and is only used as a hint when verifying - /// digital signatures. - /// - /// The new-line format, if known. - NewLineFormat? NewLineFormat { get; } - - /// - /// Get the content stream. - /// - /// - /// Gets the content stream. - /// - /// The stream. - Stream Stream { get; } - - /// - /// Open the decoded content stream. - /// - /// - /// Provides a means of reading the decoded content without having to first write it to another - /// stream using . - /// - /// The decoded content stream. - Stream Open (); - - /// - /// Decode the content stream into another stream. - /// - /// - /// If the content stream is encoded, this method will decode it into the output stream - /// using a suitable decoder based on the property, otherwise the - /// stream will be copied into the output stream as-is. - /// - /// - /// - /// - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - void DecodeTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously decode the content stream into another stream. - /// - /// - /// If the content stream is encoded, this method will decode it into the output stream - /// using a suitable decoder based on the property, otherwise the - /// stream will be copied into the output stream as-is. - /// - /// - /// - /// - /// An awaitable task. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - Task DecodeToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Copy the content stream to the specified output stream. - /// - /// - /// This is equivalent to simply using - /// to copy the content stream to the output stream except that this method is cancellable. - /// If you want the decoded content, use - /// instead. - /// - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - void WriteTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)); - - /// - /// Asynchronously copy the content stream to the specified output stream. - /// - /// - /// This is equivalent to simply using - /// to copy the content stream to the output stream except that this method is cancellable. - /// If you want the decoded content, use - /// instead. - /// - /// An awaitable task. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was cancelled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - Task WriteToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)); - } -} diff --git a/src/MimeKit/IO/BoundStream.cs b/src/MimeKit/IO/BoundStream.cs deleted file mode 100644 index 0bc2c79..0000000 --- a/src/MimeKit/IO/BoundStream.cs +++ /dev/null @@ -1,718 +0,0 @@ -// -// BoundStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MimeKit.IO { - /// - /// A bounded stream, confined to reading and writing data to a limited subset of the overall source stream. - /// - /// - /// Wraps an arbitrary stream, limiting I/O operations to a subset of the source stream. - /// If the is -1, then the end of the stream is unbound. - /// When a is set to parse a persistent stream, it will construct - /// s using bounded streams instead of loading the content into memory. - /// - public class BoundStream : Stream - { - long position; - bool disposed; - bool eos; - - /// - /// Initialize a new instance of the class. - /// - /// - /// If the is less than 0, then the end of the stream - /// is unbounded. - /// - /// The underlying stream. - /// The offset in the base stream that will mark the start of this substream. - /// The offset in the base stream that will mark the end of this substream. - /// true to leave the baseStream open after the - /// is disposed; otherwise, false. - /// - /// is null. - /// - /// - /// is less than zero. - /// -or- - /// is greater than or equal to zero - /// -and- is less than . - /// - public BoundStream (Stream baseStream, long startBoundary, long endBoundary, bool leaveOpen) - { - if (baseStream == null) - throw new ArgumentNullException (nameof (baseStream)); - - if (startBoundary < 0) - throw new ArgumentOutOfRangeException (nameof (startBoundary)); - - if (endBoundary >= 0 && endBoundary < startBoundary) - throw new ArgumentOutOfRangeException (nameof (endBoundary)); - - EndBoundary = endBoundary < 0 ? -1 : endBoundary; - StartBoundary = startBoundary; - BaseStream = baseStream; - LeaveOpen = leaveOpen; - position = 0; - eos = false; - } - - /// - /// Gets the underlying stream. - /// - /// - /// All I/O is performed on the base stream. - /// - /// The underlying stream. - public Stream BaseStream { - get; private set; - } - - /// - /// Gets the start boundary offset of the underlying stream. - /// - /// - /// The start boundary is the byte offset into the - /// that marks the beginning of the substream. - /// - /// The start boundary offset of the underlying stream. - public long StartBoundary { - get; private set; - } - - /// - /// Gets the end boundary offset of the underlying stream. - /// - /// - /// The end boundary is the byte offset into the - /// that marks the end of the substream. If the value is less than 0, - /// then the end of the stream is treated as unbound. - /// - /// The end boundary offset of the underlying stream. - public long EndBoundary { - get; private set; - } - - /// - /// Checks whether or not the underlying stream will remain open after - /// the is disposed. - /// - /// - /// Checks whether or not the underlying stream will remain open after - /// the is disposed. - /// - /// true if the underlying stream should remain open after the - /// is disposed; otherwise, false. - protected bool LeaveOpen { - get; private set; - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (BoundStream)); - } - - void CheckCanSeek () - { - if (!BaseStream.CanSeek) - throw new NotSupportedException ("The stream does not support seeking"); - } - - void CheckCanRead () - { - if (!BaseStream.CanRead) - throw new NotSupportedException ("The stream does not support reading"); - } - - void CheckCanWrite () - { - if (!BaseStream.CanWrite) - throw new NotSupportedException ("The stream does not support writing"); - } - - #region Stream implementation - - /// - /// Checks whether or not the stream supports reading. - /// - /// - /// The will only support reading if the - /// supports it. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return BaseStream.CanRead; } - } - - /// - /// Checks whether or not the stream supports writing. - /// - /// - /// The will only support writing if the - /// supports it. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return BaseStream.CanWrite; } - } - - /// - /// Checks whether or not the stream supports seeking. - /// - /// - /// The will only support seeking if the - /// supports it. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return BaseStream.CanSeek; } - } - - /// - /// Checks whether or not I/O operations can timeout. - /// - /// - /// The will only support timing out if the - /// supports it. - /// - /// true if I/O operations can timeout; otherwise, false. - public override bool CanTimeout { - get { return BaseStream.CanTimeout; } - } - - /// - /// Gets the length of the stream, in bytes. - /// - /// - /// If the property is greater than or equal to 0, - /// then the length will be calculated by subtracting the - /// from the . If the end of the stream is unbound, then the - /// will be subtracted from the length of the - /// . - /// - /// The length of the stream in bytes. - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Length { - get { - CheckDisposed (); - - if (EndBoundary != -1) - return EndBoundary - StartBoundary; - - if (eos) - return position; - - return BaseStream.Length - StartBoundary; - } - } - - /// - /// Gets or sets the current position within the stream. - /// - /// - /// The is relative to the . - /// - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return position; } - set { Seek (value, SeekOrigin.Begin); } - } - - /// - /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// - /// Gets or sets the 's read timeout. - /// - /// A value, in miliseconds, that determines how long the stream will attempt to read before timing out. - public override int ReadTimeout { - get { return BaseStream.ReadTimeout; } - set { BaseStream.ReadTimeout = value; } - } - - /// - /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// - /// Gets or sets the 's write timeout. - /// - /// A value, in miliseconds, that determines how long the stream will attempt to write before timing out. - public override int WriteTimeout { - get { return BaseStream.WriteTimeout; } - set { BaseStream.WriteTimeout = value; } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads data from the , not allowing it to - /// read beyond the . - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - CheckDisposed (); - CheckCanRead (); - - ValidateArguments (buffer, offset, count); - - // if we are at the end of the stream, we cannot read anymore data - if (EndBoundary != -1 && StartBoundary + position >= EndBoundary) { - eos = true; - return 0; - } - - // make sure that the source stream is in the expected position - if (BaseStream.Position != StartBoundary + position) - BaseStream.Seek (StartBoundary + position, SeekOrigin.Begin); - - int n = EndBoundary != -1 ? (int) Math.Min (EndBoundary - (StartBoundary + position), count) : count; - int nread = BaseStream.Read (buffer, offset, n); - - if (nread > 0) - position += nread; - else if (nread == 0) - eos = true; - - return nread; - } - - /// - /// Asynchronously reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads data from the , not allowing it to - /// read beyond the . - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanRead (); - - ValidateArguments (buffer, offset, count); - - // if we are at the end of the stream, we cannot read anymore data - if (EndBoundary != -1 && StartBoundary + position >= EndBoundary) { - eos = true; - return 0; - } - - // make sure that the source stream is in the expected position - if (BaseStream.Position != StartBoundary + position) - BaseStream.Seek (StartBoundary + position, SeekOrigin.Begin); - - int n = EndBoundary != -1 ? (int) Math.Min (EndBoundary - (StartBoundary + position), count) : count; - int nread = await BaseStream.ReadAsync (buffer, offset, n, cancellationToken).ConfigureAwait (false); - - if (nread > 0) - position += nread; - else if (nread == 0) - eos = true; - - return nread; - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes data to the , not allowing it to - /// write beyond the . - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - CheckDisposed (); - CheckCanWrite (); - - ValidateArguments (buffer, offset, count); - - // if we are at the end of the stream, we cannot write anymore data - if (EndBoundary != -1 && StartBoundary + position + count > EndBoundary) { - eos = StartBoundary + position >= EndBoundary; - throw new IOException (); - } - - // make sure that the source stream is in the expected position - if (BaseStream.Position != StartBoundary + position) - BaseStream.Seek (StartBoundary + position, SeekOrigin.Begin); - - BaseStream.Write (buffer, offset, count); - position += count; - - if (EndBoundary != -1 && StartBoundary + position >= EndBoundary) - eos = true; - } - - /// - /// Asynchronously writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes data to the , not allowing it to - /// write beyond the . - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanWrite (); - - ValidateArguments (buffer, offset, count); - - // if we are at the end of the stream, we cannot write anymore data - if (EndBoundary != -1 && StartBoundary + position + count > EndBoundary) { - eos = StartBoundary + position >= EndBoundary; - throw new IOException (); - } - - // make sure that the source stream is in the expected position - if (BaseStream.Position != StartBoundary + position) - BaseStream.Seek (StartBoundary + position, SeekOrigin.Begin); - - await BaseStream.WriteAsync (buffer, offset, count, cancellationToken).ConfigureAwait (false); - position += count; - - if (EndBoundary != -1 && StartBoundary + position >= EndBoundary) - eos = true; - } - - /// - /// Sets the position within the current stream. - /// - /// - /// Seeks within the confines of the and the . - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// is not a valid . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support seeking. - /// - /// - /// An I/O error occurred. - /// - public override long Seek (long offset, SeekOrigin origin) - { - CheckDisposed (); - CheckCanSeek (); - - long real; - - switch (origin) { - case SeekOrigin.Begin: - real = StartBoundary + offset; - break; - case SeekOrigin.Current: - real = StartBoundary + position + offset; - break; - case SeekOrigin.End: - if (offset >= 0 || (EndBoundary == -1 && !eos)) { - // We don't know if the underlying stream can seek past the end or not... - if ((real = BaseStream.Seek (offset, origin)) == -1) - return -1; - } else if (EndBoundary == -1) { - // seeking backwards from eos (which happens to be our current position) - real = StartBoundary + position + offset; - } else { - // seeking backwards from a known position - real = EndBoundary + offset; - } - - break; - default: - throw new ArgumentOutOfRangeException (nameof (origin), "Invalid SeekOrigin specified"); - } - - // sanity check the resultant offset - if (real < StartBoundary) - throw new IOException ("Cannot seek to a position before the beginning of the stream"); - - // short-cut if we are seeking to our current position - if (real == StartBoundary + position) - return position; - - if (EndBoundary != -1 && real > EndBoundary) - throw new IOException ("Cannot seek beyond the end of the stream"); - - if ((real = BaseStream.Seek (real, SeekOrigin.Begin)) == -1) - return -1; - - // reset eos if appropriate - if ((EndBoundary != -1 && real < EndBoundary) || (eos && real < StartBoundary + position)) - eos = false; - - position = real - StartBoundary; - - return position; - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Flushes the . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Flush () - { - CheckDisposed (); - CheckCanWrite (); - - BaseStream.Flush (); - } - - /// - /// Asynchronously clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Flushes the . - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task FlushAsync (CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanWrite (); - - return BaseStream.FlushAsync (cancellationToken); - } - - /// - /// Sets the length of the stream. - /// - /// - /// Updates the to be plus - /// the specified new length. If the needs to be grown - /// to allow this, then the length of the will also be - /// updated. - /// - /// The desired length of the stream in bytes. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support setting the length. - /// - /// - /// An I/O error occurred. - /// - public override void SetLength (long value) - { - CheckDisposed (); - - if (EndBoundary == -1 || StartBoundary + value > EndBoundary) { - long end = BaseStream.Length; - - if (StartBoundary + value > end) - BaseStream.SetLength (StartBoundary + value); - - EndBoundary = StartBoundary + value; - } else { - EndBoundary = StartBoundary + value; - } - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// If the property is false, then - /// the is also disposed. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !LeaveOpen) - BaseStream.Dispose (); - - base.Dispose (disposing); - disposed = true; - } - - #endregion - } -} diff --git a/src/MimeKit/IO/ChainedStream.cs b/src/MimeKit/IO/ChainedStream.cs deleted file mode 100644 index 7561e4c..0000000 --- a/src/MimeKit/IO/ChainedStream.cs +++ /dev/null @@ -1,700 +0,0 @@ -// -// ChainedStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -namespace MimeKit.IO { - /// - /// A chained stream. - /// - /// - /// Chains multiple streams together such that reading or writing beyond the end - /// of one stream spills over into the next stream in the chain. The idea is to - /// make it appear is if the chain of streams is all one continuous stream. - /// - public class ChainedStream : Stream - { - readonly List streams; - readonly List leaveOpen; - long position; - bool disposed; - int current; - bool eos; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public ChainedStream () - { - leaveOpen = new List (); - streams = new List (); - } - - /// - /// Add the specified stream to the chained stream. - /// - /// - /// Adds the stream to the end of the chain. - /// - /// The stream. - /// true if the - /// should remain open after the is disposed; - /// otherwise, false. - /// - /// is null. - /// - public void Add (Stream stream, bool leaveOpen = false) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - this.leaveOpen.Add (leaveOpen); - streams.Add (stream); - eos = false; - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (ChainedStream)); - } - - void CheckCanSeek () - { - if (!CanSeek) - throw new NotSupportedException ("The stream does not support seeking"); - } - - void CheckCanRead () - { - if (!CanRead) - throw new NotSupportedException ("The stream does not support reading"); - } - - void CheckCanWrite () - { - if (!CanWrite) - throw new NotSupportedException ("The stream does not support writing"); - } - - /// - /// Checks whether or not the stream supports reading. - /// - /// - /// The only supports reading if all of its - /// streams support it. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { - foreach (var stream in streams) { - if (!stream.CanRead) - return false; - } - - return streams.Count > 0; - } - } - - /// - /// Checks whether or not the stream supports writing. - /// - /// - /// The only supports writing if all of its - /// streams support it. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { - foreach (var stream in streams) { - if (!stream.CanWrite) - return false; - } - - return streams.Count > 0; - } - } - - /// - /// Checks whether or not the stream supports seeking. - /// - /// - /// The only supports seeking if all of its - /// streams support it. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { - foreach (var stream in streams) { - if (!stream.CanSeek) - return false; - } - - return streams.Count > 0; - } - } - - /// - /// Checks whether or not I/O operations can timeout. - /// - /// - /// The only supports timeouts if all of its - /// streams support them. - /// - /// true if I/O operations can timeout; otherwise, false. - public override bool CanTimeout { - get { return false; } - } - - /// - /// Gets the length of the stream, in bytes. - /// - /// - /// The length of a is the combined lenths of all - /// of its chained streams. - /// - /// The length of the stream in bytes. - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Length { - get { - long length = 0; - - CheckDisposed (); - - foreach (var stream in streams) - length += stream.Length; - - return length; - } - } - - /// - /// Gets or sets the current position within the stream. - /// - /// - /// It is always possible to get the position of a , - /// but setting the position is only possible if all of its streams are seekable. - /// - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return position; } - set { Seek (value, SeekOrigin.Begin); } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads up to the requested number of bytes if reading is supported. If the - /// current child stream does not have enough remaining data to complete the - /// read, the read will progress into the next stream in the chain in order - /// to complete the read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - CheckDisposed (); - CheckCanRead (); - - ValidateArguments (buffer, offset, count); - - if (count == 0 || eos) - return 0; - - int n, nread = 0; - - while (current < streams.Count) { - while (nread < count && (n = streams[current].Read (buffer, offset + nread, count - nread)) > 0) - nread += n; - - if (nread == count) - break; - - current++; - } - - if (nread > 0) - position += nread; - else - eos = true; - - return nread; - } - - /// - /// Asynchronously reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads up to the requested number of bytes if reading is supported. If the - /// current child stream does not have enough remaining data to complete the - /// read, the read will progress into the next stream in the chain in order - /// to complete the read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanRead (); - - ValidateArguments (buffer, offset, count); - - if (count == 0 || eos) - return 0; - - int n, nread = 0; - - while (current < streams.Count) { - while (nread < count) { - if ((n = await streams[current].ReadAsync (buffer, offset + nread, count - nread, cancellationToken).ConfigureAwait (false)) <= 0) - break; - - nread += n; - } - - if (nread == count) - break; - - current++; - } - - if (nread > 0) - position += nread; - else - eos = true; - - return nread; - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes the requested number of bytes if writing is supported. If the - /// current child stream does not have enough remaining space to fit the - /// complete buffer, the data will spill over into the next stream in the - /// chain in order to complete the write. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - CheckDisposed (); - CheckCanWrite (); - - ValidateArguments (buffer, offset, count); - - if (current >= streams.Count) - current = streams.Count - 1; - - int nwritten = 0; - - while (current < streams.Count && nwritten < count) { - int n = count - nwritten; - - if (current + 1 < streams.Count) { - long left = streams[current].Length - streams[current].Position; - - if (left < n) - n = (int) left; - } - - streams[current].Write (buffer, offset + nwritten, n); - position += n; - nwritten += n; - - if (nwritten < count) { - streams[current].Flush (); - current++; - } - } - } - - /// - /// Asynchronously writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes the requested number of bytes if writing is supported. If the - /// current child stream does not have enough remaining space to fit the - /// complete buffer, the data will spill over into the next stream in the - /// chain in order to complete the write. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override async Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanWrite (); - - ValidateArguments (buffer, offset, count); - - if (current >= streams.Count) - current = streams.Count - 1; - - int nwritten = 0; - - while (current < streams.Count && nwritten < count) { - int n = count - nwritten; - - if (current + 1 < streams.Count) { - long left = streams[current].Length - streams[current].Position; - - if (left < n) - n = (int) left; - } - - await streams[current].WriteAsync (buffer, offset + nwritten, n, cancellationToken).ConfigureAwait (false); - position += n; - nwritten += n; - - if (nwritten < count) { - await streams[current].FlushAsync (cancellationToken).ConfigureAwait (false); - current++; - } - } - } - - /// - /// Sets the position within the current stream. - /// - /// - /// Seeks to the specified position within the stream if all child streams - /// support seeking. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// is not a valid . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support seeking. - /// - /// - /// An I/O error occurred. - /// - public override long Seek (long offset, SeekOrigin origin) - { - CheckDisposed (); - CheckCanSeek (); - - long length = -1; - long real; - - switch (origin) { - case SeekOrigin.Begin: - real = offset; - break; - case SeekOrigin.Current: - real = position + offset; - break; - case SeekOrigin.End: - length = Length; - real = length + offset; - break; - default: - throw new ArgumentOutOfRangeException (nameof (origin), "Invalid SeekOrigin specified"); - } - - // sanity check the resultant offset - if (real < 0) - throw new IOException ("Cannot seek to a position before the beginning of the stream"); - - // short-cut if we are seeking to our current position - if (real == position) - return position; - - if (real > (length < 0 ? Length : length)) - throw new IOException ("Cannot seek beyond the end of the stream"); - - if (real > position) { - while (current < streams.Count && position < real) { - long left = streams[current].Length - streams[current].Position; - long n = Math.Min (left, real - position); - - streams[current].Seek (n, SeekOrigin.Current); - position += n; - - if (position < real) - current++; - } - - eos = current >= streams.Count; - } else { - int max = Math.Min (streams.Count - 1, current); - int cur = 0; - - position = 0; - while (cur <= max) { - length = streams[cur].Length; - - if (real < position + length) { - // this is the stream which encompasses our seek offset - streams[cur].Seek (real - position, SeekOrigin.Begin); - position = real; - break; - } - - position += length; - cur++; - } - - current = cur++; - - // reset any streams between our new current stream and our old current stream - while (cur <= max) - streams[cur++].Seek (0, SeekOrigin.Begin); - - eos = false; - } - - return position; - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// If all of the child streams support writing, then the current child stream - /// will be flushed. - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Flush () - { - CheckDisposed (); - CheckCanWrite (); - - if (current < streams.Count) - streams[current].Flush (); - } - - /// - /// Asynchronously clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// If all of the child streams support writing, then the current child stream - /// will be flushed. - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task FlushAsync (CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanWrite (); - - if (current < streams.Count) - await streams[current].FlushAsync (cancellationToken).ConfigureAwait (false); - } - - /// - /// Sets the length of the stream. - /// - /// - /// Setting the length of a is not supported. - /// - /// The desired length of the stream in bytes. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support setting the length. - /// - public override void SetLength (long value) - { - CheckDisposed (); - - throw new NotSupportedException ("Cannot set a length on the stream"); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - for (int i = 0; i < streams.Count; i++) { - if (!leaveOpen[i]) - streams[i].Dispose (); - } - } - - base.Dispose (disposing); - disposed = true; - } - } -} diff --git a/src/MimeKit/IO/FilteredStream.cs b/src/MimeKit/IO/FilteredStream.cs deleted file mode 100644 index 604bc8f..0000000 --- a/src/MimeKit/IO/FilteredStream.cs +++ /dev/null @@ -1,812 +0,0 @@ -// -// FilteredStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit.IO.Filters; - -namespace MimeKit.IO { - /// - /// A stream which filters data as it is read or written. - /// - /// - /// Passes data through each as the data is read or written. - /// - public class FilteredStream : Stream, ICancellableStream - { - const int ReadBufferSize = 4096; - - enum IOOperation : byte { - Read, - Write - } - - List filters = new List (); - IOOperation lastOp = IOOperation.Write; - int filteredLength; - int filteredIndex; - byte[] filtered; - byte[] readbuf; - bool disposed; - bool flushed; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a filtered stream using the specified source stream. - /// - /// The underlying stream to filter. - /// - /// is null. - /// - public FilteredStream (Stream source) - { - if (source == null) - throw new ArgumentNullException (nameof (source)); - - Source = source; - } - - /// - /// Gets the underlying source stream. - /// - /// - /// In general, it is not a good idea to manipulate the underlying - /// source stream because most s store - /// important state about previous bytes read from or written to - /// the source stream. - /// - /// The underlying source stream. - public Stream Source { - get; private set; - } - - /// - /// Adds the specified filter. - /// - /// - /// Adds the to the end of the list of filters - /// that data will pass through as data is read from or written to the - /// underlying source stream. - /// - /// The filter. - /// - /// is null. - /// - public void Add (IMimeFilter filter) - { - if (filter == null) - throw new ArgumentNullException (nameof (filter)); - - filters.Add (filter); - } - - /// - /// Checks if the filtered stream contains the specified filter. - /// - /// - /// Determines whether or not the filtered stream contains the specified filter. - /// - /// true if the specified filter exists; - /// otherwise false. - /// The filter. - /// - /// is null. - /// - public bool Contains (IMimeFilter filter) - { - if (filter == null) - throw new ArgumentNullException (nameof (filter)); - - return filters.Contains (filter); - } - - /// - /// Remove the specified filter. - /// - /// - /// Removes the specified filter from the list if it exists. - /// - /// true if the filter was removed; otherwise false. - /// The filter. - /// - /// is null. - /// - public bool Remove (IMimeFilter filter) - { - if (filter == null) - throw new ArgumentNullException (nameof (filter)); - - return filters.Remove (filter); - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (FilteredStream)); - } - - void CheckCanRead () - { - if (!Source.CanRead) - throw new NotSupportedException ("The stream does not support reading"); - } - - void CheckCanWrite () - { - if (!Source.CanWrite) - throw new NotSupportedException ("The stream does not support writing"); - } - - #region Stream implementation - - /// - /// Checks whether or not the stream supports reading. - /// - /// - /// The will only support reading if the - /// supports it. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return Source.CanRead; } - } - - /// - /// Checks whether or not the stream supports writing. - /// - /// - /// The will only support writing if the - /// supports it. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return Source.CanWrite; } - } - - /// - /// Checks whether or not the stream supports seeking. - /// - /// - /// Seeking is not supported by the . - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return false; } - } - - /// - /// Checks whether or not I/O operations can timeout. - /// - /// - /// The will only support timing out if the - /// supports it. - /// - /// true if I/O operations can timeout; otherwise, false. - public override bool CanTimeout { - get { return Source.CanTimeout; } - } - - /// - /// Gets the length of the stream, in bytes. - /// - /// - /// Getting the length of a is not supported. - /// - /// The length of the stream in bytes. - /// - /// The stream does not support seeking. - /// - public override long Length { - get { throw new NotSupportedException ("Cannot get the length of the stream"); } - } - - /// - /// Gets or sets the current position within the stream. - /// - /// - /// Getting and setting the position of a is not supported. - /// - /// The position of the stream. - /// - /// The stream does not support seeking. - /// - public override long Position { - get { throw new NotSupportedException ("The stream does not support seeking"); } - set { throw new NotSupportedException ("The stream does not support seeking"); } - } - - /// - /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to read before timing out. - /// - /// - /// Gets or sets the read timeout on the stream. - /// - /// A value, in miliseconds, that determines how long the stream will attempt to read before timing out. - public override int ReadTimeout - { - get { return Source.ReadTimeout; } - set { Source.ReadTimeout = value; } - } - - /// - /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to write before timing out. - /// - /// - /// Gets or sets the write timeout on the stream. - /// - /// A value, in miliseconds, that determines how long the stream will attempt to write before timing out. - public override int WriteTimeout - { - get { return Source.WriteTimeout; } - set { Source.WriteTimeout = value; } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads up to the requested number of bytes, passing the data read from the stream - /// through each of the filters before finally copying the result into the provided buffer. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public int Read (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanRead (); - - ValidateArguments (buffer, offset, count); - - lastOp = IOOperation.Read; - if (readbuf == null) - readbuf = new byte[ReadBufferSize]; - - int nread; - - if (filteredLength == 0) { - var cancellable = Source as ICancellableStream; - - if (cancellable != null) { - if ((nread = cancellable.Read (readbuf, 0, ReadBufferSize, cancellationToken)) <= 0) - return nread; - } else { - cancellationToken.ThrowIfCancellationRequested (); - if ((nread = Source.Read (readbuf, 0, ReadBufferSize)) <= 0) - return nread; - } - - // filter the data we've just read... - filteredLength = nread; - filteredIndex = 0; - filtered = readbuf; - - foreach (var filter in filters) - filtered = filter.Filter (filtered, filteredIndex, filteredLength, out filteredIndex, out filteredLength); - } - - // copy our filtered data into our caller's buffer - nread = Math.Min (filteredLength, count); - - if (nread > 0) { - Buffer.BlockCopy (filtered, filteredIndex, buffer, offset, nread); - filteredLength -= nread; - filteredIndex += nread; - } - - return nread; - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads up to the requested number of bytes, passing the data read from the stream - /// through each of the filters before finally copying the result into the provided buffer. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - return Read (buffer, offset, count, CancellationToken.None); - } - - /// - /// Asynchronously reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads up to the requested number of bytes, passing the data read from the stream - /// through each of the filters before finally copying the result into the provided buffer. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanRead (); - - ValidateArguments (buffer, offset, count); - - lastOp = IOOperation.Read; - if (readbuf == null) - readbuf = new byte[ReadBufferSize]; - - int nread; - - if (filteredLength == 0) { - if ((nread = await Source.ReadAsync (readbuf, 0, ReadBufferSize, cancellationToken).ConfigureAwait (false)) <= 0) - return nread; - - // filter the data we've just read... - filteredLength = nread; - filteredIndex = 0; - filtered = readbuf; - - foreach (var filter in filters) - filtered = filter.Filter (filtered, filteredIndex, filteredLength, out filteredIndex, out filteredLength); - } - - // copy our filtered data into our caller's buffer - nread = Math.Min (filteredLength, count); - - if (nread > 0) { - Buffer.BlockCopy (filtered, filteredIndex, buffer, offset, nread); - filteredLength -= nread; - filteredIndex += nread; - } - - return nread; - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Filters the provided buffer through each of the filters before finally writing - /// the result to the underlying stream. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void Write (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanWrite (); - - ValidateArguments (buffer, offset, count); - - lastOp = IOOperation.Write; - flushed = false; - - filteredIndex = offset; - filteredLength = count; - filtered = buffer; - - foreach (var filter in filters) - filtered = filter.Filter (filtered, filteredIndex, filteredLength, out filteredIndex, out filteredLength); - - if (filteredLength == 0) - return; - - var cancellable = Source as ICancellableStream; - - if (cancellable != null) { - cancellable.Write (filtered, filteredIndex, filteredLength, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - Source.Write (filtered, filteredIndex, filteredLength); - } - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Filters the provided buffer through each of the filters before finally writing - /// the result to the underlying stream. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - Write (buffer, offset, count, CancellationToken.None); - } - - /// - /// Asynchronously writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Filters the provided buffer through each of the filters before finally writing - /// the result to the underlying stream. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanWrite (); - - ValidateArguments (buffer, offset, count); - - lastOp = IOOperation.Write; - flushed = false; - - filteredIndex = offset; - filteredLength = count; - filtered = buffer; - - foreach (var filter in filters) - filtered = filter.Filter (filtered, filteredIndex, filteredLength, out filteredIndex, out filteredLength); - - return Source.WriteAsync (filtered, filteredIndex, filteredLength, cancellationToken); - } - - /// - /// Sets the position within the current stream. - /// - /// - /// Seeking is not supported by the . - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// The stream does not support seeking. - /// - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException ("The stream does not support seeking"); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Flushes the state of all filters, writing any output to the underlying - /// stream and then calling on the . - /// - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void Flush (CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanWrite (); - - if (lastOp == IOOperation.Read) - return; - - if (!flushed) { - filtered = new byte[0]; - filteredIndex = 0; - filteredLength = 0; - - foreach (var filter in filters) - filtered = filter.Flush (filtered, filteredIndex, filteredLength, out filteredIndex, out filteredLength); - - flushed = true; - } - - var cancellable = Source as ICancellableStream; - - if (filteredLength > 0) { - if (cancellable != null) { - cancellable.Write (filtered, filteredIndex, filteredLength, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - Source.Write (filtered, filteredIndex, filteredLength); - } - - filteredIndex = 0; - filteredLength = 0; - } - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Flushes the state of all filters, writing any output to the underlying - /// stream and then calling on the . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Flush () - { - Flush (CancellationToken.None); - } - - /// - /// Asynchronously clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Flushes the state of all filters, writing any output to the underlying - /// stream and then calling on the . - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task FlushAsync (CancellationToken cancellationToken) - { - CheckDisposed (); - CheckCanWrite (); - - if (lastOp == IOOperation.Read) - return; - - if (!flushed) { - filtered = new byte[0]; - filteredIndex = 0; - filteredLength = 0; - - foreach (var filter in filters) - filtered = filter.Flush (filtered, filteredIndex, filteredLength, out filteredIndex, out filteredLength); - - flushed = true; - } - - if (filteredLength > 0) { - await Source.WriteAsync (filtered, filteredIndex, filteredLength, cancellationToken).ConfigureAwait (false); - - filteredIndex = 0; - filteredLength = 0; - } - } - - /// - /// Sets the length of the stream. - /// - /// - /// Setting the length of a is not supported. - /// - /// The desired length of the stream in bytes. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support setting the length. - /// - public override void SetLength (long value) - { - CheckDisposed (); - - throw new NotSupportedException ("Cannot set a length on the stream"); - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing) { - if (filters != null) { - filters.Clear (); - filters = null; - } - - readbuf = null; - } - - base.Dispose (disposing); - disposed = true; - } - - #endregion - } -} diff --git a/src/MimeKit/IO/Filters/ArmoredFromFilter.cs b/src/MimeKit/IO/Filters/ArmoredFromFilter.cs deleted file mode 100644 index 48237a2..0000000 --- a/src/MimeKit/IO/Filters/ArmoredFromFilter.cs +++ /dev/null @@ -1,167 +0,0 @@ -// -// ArmoredFromFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; - -namespace MimeKit.IO.Filters { - /// - /// A filter that armors lines beginning with "From " by encoding the 'F' with the - /// Quoted-Printable encoding. - /// - /// - /// From-armoring is a workaround to prevent receiving clients (or servers) - /// that uses the mbox file format for local storage from munging the line - /// by prepending a ">", as is typical with the mbox format. - /// This armoring technique ensures that the receiving client will still - /// be able to verify S/MIME signatures. - /// - public class ArmoredFromFilter : MimeFilterBase - { - const string From = "From "; - bool midline; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public ArmoredFromFilter () - { - } - - static bool StartsWithFrom (byte[] input, int startIndex, int endIndex) - { - for (int i = 0, index = startIndex; i < From.Length && index < endIndex; i++, index++) { - if (input[index] != (byte) From[i]) - return false; - } - - return true; - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - var fromOffsets = new List (); - int endIndex = startIndex + length; - int index = startIndex; - int left; - - while (index < endIndex) { - byte c = 0; - - if (midline) { - while (index < endIndex) { - c = input[index++]; - if (c == (byte) '\n') - break; - } - } - - if (c == (byte) '\n' || !midline) { - if ((left = endIndex - index) > 0) { - midline = true; - - if (left < 5) { - if (StartsWithFrom (input, index, endIndex)) { - SaveRemainingInput (input, index, left); - endIndex = index; - midline = false; - break; - } - } else { - if (StartsWithFrom (input, index, endIndex)) { - fromOffsets.Add (index); - index += 5; - } - } - } else { - midline = false; - } - } - } - - if (fromOffsets.Count > 0) { - int need = (endIndex - startIndex) + fromOffsets.Count * 2; - - EnsureOutputSize (need, false); - outputLength = 0; - outputIndex = 0; - - index = startIndex; - foreach (var offset in fromOffsets) { - if (index < offset) { - Buffer.BlockCopy (input, index, OutputBuffer, outputLength, offset - index); - outputLength += offset - index; - index = offset; - } - - // encode the F using quoted-printable - OutputBuffer[outputLength++] = (byte) '='; - OutputBuffer[outputLength++] = (byte) '4'; - OutputBuffer[outputLength++] = (byte) '6'; - index++; - } - - Buffer.BlockCopy (input, index, OutputBuffer, outputLength, endIndex - index); - outputLength += endIndex - index; - - return OutputBuffer; - } - - outputLength = endIndex - startIndex; - outputIndex = 0; - return input; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - midline = false; - base.Reset (); - } - } -} diff --git a/src/MimeKit/IO/Filters/BestEncodingFilter.cs b/src/MimeKit/IO/Filters/BestEncodingFilter.cs deleted file mode 100644 index 557d851..0000000 --- a/src/MimeKit/IO/Filters/BestEncodingFilter.cs +++ /dev/null @@ -1,273 +0,0 @@ -// -// BestEncodingFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.IO.Filters { - /// - /// A filter that can be used to determine the most efficient Content-Transfer-Encoding. - /// - /// - /// Keeps track of the content that gets passed through the filter in order to - /// determine the most efficient to use. - /// - public class BestEncodingFilter : IMimeFilter - { - readonly byte[] marker = new byte[6]; - int maxline, linelen; - int count0, count8; - int markerLength; - bool hasMarker; - int total; - byte pc; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public BestEncodingFilter () - { - } - - /// - /// Gets the best encoding given the specified constraints. - /// - /// - /// Gets the best encoding given the specified constraints. - /// - /// The best encoding. - /// The encoding constraint. - /// The maximum allowable line length (not counting the CRLF). Must be between 60 and 998 (inclusive). - /// - /// is not between 60 and 998 (inclusive). - /// -or- - /// is not a valid value. - /// - public ContentEncoding GetBestEncoding (EncodingConstraint constraint, int maxLineLength = 78) - { - if (maxLineLength < FormatOptions.MinimumLineLength || maxLineLength > FormatOptions.MaximumLineLength) - throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - - switch (constraint) { - case EncodingConstraint.SevenBit: - if (count0 > 0) - return ContentEncoding.Base64; - - if (count8 > 0) { - if (count8 >= (int) (total * (17.0 / 100.0))) - return ContentEncoding.Base64; - - return ContentEncoding.QuotedPrintable; - } - - if (hasMarker || maxline > maxLineLength) - return ContentEncoding.QuotedPrintable; - - break; - case EncodingConstraint.EightBit: - if (count0 > 0) - return ContentEncoding.Base64; - - if (hasMarker || maxline > maxLineLength) - return ContentEncoding.QuotedPrintable; - - if (count8 > 0) - return ContentEncoding.EightBit; - - break; - case EncodingConstraint.None: - if (hasMarker || maxline > maxLineLength) { - if (count8 > (int) (total * (17.0 / 100.0))) - return ContentEncoding.Base64; - - return ContentEncoding.QuotedPrintable; - } - - if (count0 > 0) - return ContentEncoding.Binary; - - if (count8 > 0) - return ContentEncoding.EightBit; - - break; - default: - throw new ArgumentOutOfRangeException (nameof (constraint)); - } - - return ContentEncoding.SevenBit; - } - - #region IMimeFilter implementation - - static unsafe bool IsMboxMarker (byte[] marker) - { - const uint FromMask = 0xFFFFFFFF; - const uint From = 0x6D6F7246; - - fixed (byte* buf = marker) { - uint* word = (uint*) buf; - - if ((*word & FromMask) != From) - return false; - - return *(buf + 4) == (byte) ' '; - } - } - - unsafe void Scan (byte* inptr, byte* inend) - { - while (inptr < inend) { - byte c = 0; - - while (inptr < inend && (c = *inptr++) != (byte) '\n') { - if (c == 0) - count0++; - else if ((c & 0x80) != 0) - count8++; - - if (!hasMarker && markerLength < 5) - marker[markerLength++] = c; - - linelen++; - pc = c; - } - - if (c == (byte) '\n') { - if (pc == (byte) '\r') - linelen--; - - maxline = Math.Max (maxline, linelen); - linelen = 0; - - // check our from-save buffer for "From " - if (!hasMarker && markerLength == 5 && IsMboxMarker (marker)) - hasMarker = true; - - markerLength = 0; - } - } - } - - static void ValidateArguments (byte[] input, int startIndex, int length) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - } - - /// - /// Filters the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes of the input to filter. - /// The starting index of the output in the returned buffer. - /// The length of the output buffer. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength) - { - ValidateArguments (input, startIndex, length); - - unsafe { - fixed (byte* inptr = input) { - Scan (inptr + startIndex, inptr + startIndex + length); - } - } - - maxline = Math.Max (maxline, linelen); - total += length; - - outputIndex = startIndex; - outputLength = length; - - return input; - } - - /// - /// Filters the specified input, flushing all internally buffered data to the output. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes of the input to filter. - /// The starting index of the output in the returned buffer. - /// The length of the output buffer. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public byte[] Flush (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength) - { - return Filter (input, startIndex, length, out outputIndex, out outputLength); - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public void Reset () - { - hasMarker = false; - markerLength = 0; - linelen = 0; - maxline = 0; - count0 = 0; - count8 = 0; - total = 0; - pc = 0; - } - - #endregion - } -} - diff --git a/src/MimeKit/IO/Filters/CharsetFilter.cs b/src/MimeKit/IO/Filters/CharsetFilter.cs deleted file mode 100644 index d673fe3..0000000 --- a/src/MimeKit/IO/Filters/CharsetFilter.cs +++ /dev/null @@ -1,229 +0,0 @@ -// -// CharsetFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -using MimeKit.Utils; - -namespace MimeKit.IO.Filters { - /// - /// A charset filter for incrementally converting text streams from - /// one charset encoding to another. - /// - /// - /// Incrementally converts text from one charset encoding to another. - /// - public class CharsetFilter : MimeFilterBase - { - readonly char[] chars = new char[1024]; - readonly Decoder decoder; - readonly Encoder encoder; - - static Encoding GetEncoding (string paramName, string encodingName) - { - if (encodingName == null) - throw new ArgumentNullException (paramName); - - return CharsetUtils.GetEncoding (encodingName); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new to convert text from the specified - /// source encoding into the target charset encoding. - /// - /// Source encoding name. - /// Target encoding name. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The is not supported by the system. - /// -or- - /// The is not supported by the system. - /// - public CharsetFilter (string sourceEncodingName, string targetEncodingName) - : this (GetEncoding ("sourceEncodingName", sourceEncodingName), - GetEncoding ("targetEncodingName", targetEncodingName)) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new to convert text from the specified - /// source encoding into the target charset encoding. - /// - /// Source code page. - /// Target code page. - /// - /// is less than zero or greater than 65535. - /// -or- - /// is less than zero or greater than 65535. - /// - /// - /// The is not supported by the system. - /// -or- - /// The is not supported by the system. - /// - public CharsetFilter (int sourceCodePage, int targetCodePage) - : this (Encoding.GetEncoding (sourceCodePage), Encoding.GetEncoding (targetCodePage)) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new to convert text from the specified - /// source encoding into the target charset encoding. - /// - /// Source encoding. - /// Target encoding. - /// - /// is null. - /// -or- - /// is null. - /// - public CharsetFilter (Encoding sourceEncoding, Encoding targetEncoding) - { - if (sourceEncoding == null) - throw new ArgumentNullException (nameof (sourceEncoding)); - - if (targetEncoding == null) - throw new ArgumentNullException (nameof (targetEncoding)); - - SourceEncoding = sourceEncoding; - TargetEncoding = targetEncoding; - - decoder = (Decoder) SourceEncoding.GetDecoder (); - encoder = (Encoder) TargetEncoding.GetEncoder (); - } - - /// - /// Gets the source encoding. - /// - /// - /// Gets the source encoding. - /// - /// The source encoding. - public Encoding SourceEncoding { - get; private set; - } - - /// - /// Gets the target encoding. - /// - /// - /// Gets the target encoding. - /// - /// The target encoding. - public Encoding TargetEncoding { - get; private set; - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - int inputIndex = startIndex; - int inputLeft = length; - bool decoded = false; - bool encoded = false; - int nwritten, nread; - int outputOffset = 0; - int outputLeft; - int charIndex; - int charsLeft; - - do { - charsLeft = chars.Length; - charIndex = 0; - - if (!decoded && inputLeft > 0) { - decoder.Convert (input, inputIndex, inputLeft, chars, charIndex, charsLeft, flush, out nread, out nwritten, out decoded); - if (nwritten > 0) - encoded = false; - charIndex += nwritten; - inputIndex += nread; - inputLeft -= nread; - } else { - decoded = true; - } - - charsLeft = charIndex; - charIndex = 0; - - // encode *all* input chars into the output buffer - while (!encoded) { - EnsureOutputSize (outputOffset + TargetEncoding.GetMaxByteCount (charsLeft) + 4, true); - outputLeft = OutputBuffer.Length - outputOffset; - - encoder.Convert (chars, charIndex, charsLeft, OutputBuffer, outputOffset, outputLeft, flush, out nread, out nwritten, out encoded); - outputOffset += nwritten; - charIndex += nread; - charsLeft -= nread; - } - } while (!decoded); - - outputLength = outputOffset; - outputIndex = 0; - - return OutputBuffer; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - decoder.Reset (); - encoder.Reset (); - base.Reset (); - } - } -} diff --git a/src/MimeKit/IO/Filters/DecoderFilter.cs b/src/MimeKit/IO/Filters/DecoderFilter.cs deleted file mode 100644 index c85cc49..0000000 --- a/src/MimeKit/IO/Filters/DecoderFilter.cs +++ /dev/null @@ -1,159 +0,0 @@ -// -// DecoderFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; -using MimeKit.Encodings; - -namespace MimeKit.IO.Filters { - /// - /// A filter for decoding MIME content. - /// - /// - /// Uses a to incrementally decode data. - /// - public class DecoderFilter : MimeFilterBase - { - /// - /// Gets the decoder used by this filter. - /// - /// - /// Gets the decoder used by this filter. - /// - /// The decoder. - public IMimeDecoder Decoder { - get; private set; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the decoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return Decoder.Encoding; } - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new using the specified decoder. - /// - /// A specific decoder for the filter to use. - /// - /// is null. - /// - public DecoderFilter (IMimeDecoder decoder) - { - if (decoder == null) - throw new ArgumentNullException (nameof (decoder)); - - Decoder = decoder; - } - - /// - /// Create a filter that will decode the specified encoding. - /// - /// - /// Creates a new for the specified encoding. - /// - /// A new decoder filter. - /// The encoding to create a filter for. - public static IMimeFilter Create (ContentEncoding encoding) - { - switch (encoding) { - case ContentEncoding.Base64: return new DecoderFilter (new Base64Decoder ()); - case ContentEncoding.QuotedPrintable: return new DecoderFilter (new QuotedPrintableDecoder ()); - case ContentEncoding.UUEncode: return new DecoderFilter (new UUDecoder ()); - default: return new PassThroughFilter (); - } - } - - /// - /// Create a filter that will decode the specified encoding. - /// - /// - /// Creates a new for the specified encoding. - /// - /// A new decoder filter. - /// The name of the encoding to create a filter for. - /// - /// is null. - /// - public static IMimeFilter Create (string name) - { - ContentEncoding encoding; - - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (!MimeUtils.TryParse (name, out encoding)) - encoding = ContentEncoding.Default; - - return Create (encoding); - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - EnsureOutputSize (Decoder.EstimateOutputLength (length), false); - - outputLength = Decoder.Decode (input, startIndex, length, OutputBuffer); - outputIndex = 0; - - return OutputBuffer; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - Decoder.Reset (); - base.Reset (); - } - } -} diff --git a/src/MimeKit/IO/Filters/Dos2UnixFilter.cs b/src/MimeKit/IO/Filters/Dos2UnixFilter.cs deleted file mode 100644 index 1497834..0000000 --- a/src/MimeKit/IO/Filters/Dos2UnixFilter.cs +++ /dev/null @@ -1,121 +0,0 @@ -// -// Dos2UnixFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.IO.Filters { - /// - /// A filter that will convert from Windows/DOS line endings to Unix line endings. - /// - /// - /// Converts from Windows/DOS line endings to Unix line endings. - /// - public class Dos2UnixFilter : MimeFilterBase - { - readonly bool ensureNewLine; - byte pc; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// Ensure that the stream ends with a new line. - public Dos2UnixFilter (bool ensureNewLine = false) - { - this.ensureNewLine = ensureNewLine; - } - - unsafe int Filter (byte* inbuf, int length, byte* outbuf, bool flush) - { - byte* inend = inbuf + length; - byte* outptr = outbuf; - byte* inptr = inbuf; - - while (inptr < inend) { - if (*inptr == (byte) '\n') { - *outptr++ = *inptr; - } else { - if (pc == (byte) '\r') - *outptr++ = pc; - - if (*inptr != (byte) '\r') - *outptr++ = *inptr; - } - - pc = *inptr++; - } - - if (flush && ensureNewLine && pc != (byte) '\n') - *outptr++ = (byte) '\n'; - - return (int) (outptr - outbuf); - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - if (pc == (byte) '\r') - EnsureOutputSize (length + (flush && ensureNewLine ? 2 : 1), false); - else - EnsureOutputSize (length + (flush && ensureNewLine ? 1 : 0), false); - - outputIndex = 0; - - unsafe { - fixed (byte* inptr = input, outptr = OutputBuffer) { - outputLength = Filter (inptr + startIndex, length, outptr, flush); - } - } - - return OutputBuffer; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - pc = 0; - base.Reset (); - } - } -} diff --git a/src/MimeKit/IO/Filters/EncoderFilter.cs b/src/MimeKit/IO/Filters/EncoderFilter.cs deleted file mode 100644 index 8f261bc..0000000 --- a/src/MimeKit/IO/Filters/EncoderFilter.cs +++ /dev/null @@ -1,163 +0,0 @@ -// -// EncoderFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Utils; -using MimeKit.Encodings; - -namespace MimeKit.IO.Filters { - /// - /// A filter for encoding MIME content. - /// - /// - /// Uses a to incrementally encode data. - /// - public class EncoderFilter : MimeFilterBase - { - /// - /// Gets the encoder used by this filter. - /// - /// - /// Gets the encoder used by this filter. - /// - /// The encoder. - public IMimeEncoder Encoder { - get; private set; - } - - /// - /// Gets the encoding. - /// - /// - /// Gets the encoding that the encoder supports. - /// - /// The encoding. - public ContentEncoding Encoding { - get { return Encoder.Encoding; } - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new using the specified encoder. - /// - /// A specific encoder for the filter to use. - /// - /// is null. - /// - public EncoderFilter (IMimeEncoder encoder) - { - if (encoder == null) - throw new ArgumentNullException (nameof (encoder)); - - Encoder = encoder; - } - - /// - /// Create a filter that will encode using specified encoding. - /// - /// - /// Creates a new for the specified encoding. - /// - /// A new encoder filter. - /// The encoding to create a filter for. - public static IMimeFilter Create (ContentEncoding encoding) - { - switch (encoding) { - case ContentEncoding.Base64: return new EncoderFilter (new Base64Encoder ()); - case ContentEncoding.QuotedPrintable: return new EncoderFilter (new QuotedPrintableEncoder ()); - case ContentEncoding.UUEncode: return new EncoderFilter (new UUEncoder ()); - default: return new PassThroughFilter (); - } - } - - /// - /// Create a filter that will encode using specified encoding. - /// - /// - /// Creates a new for the specified encoding. - /// - /// A new encoder filter. - /// The name of the encoding to create a filter for. - /// - /// is null. - /// - public static IMimeFilter Create (string name) - { - ContentEncoding encoding; - - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (!MimeUtils.TryParse (name, out encoding)) - encoding = ContentEncoding.Default; - - return Create (encoding); - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - EnsureOutputSize (Encoder.EstimateOutputLength (length), false); - - if (flush) - outputLength = Encoder.Flush (input, startIndex, length, OutputBuffer); - else - outputLength = Encoder.Encode (input, startIndex, length, OutputBuffer); - - outputIndex = 0; - - return OutputBuffer; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - Encoder.Reset (); - base.Reset (); - } - } -} diff --git a/src/MimeKit/IO/Filters/IMimeFilter.cs b/src/MimeKit/IO/Filters/IMimeFilter.cs deleted file mode 100644 index 0154742..0000000 --- a/src/MimeKit/IO/Filters/IMimeFilter.cs +++ /dev/null @@ -1,74 +0,0 @@ -// -// IMimeFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.IO.Filters { - /// - /// An interface for incrementally filtering data. - /// - /// - /// An interface for incrementally filtering data. - /// - public interface IMimeFilter - { - /// - /// Filters the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes of the input to filter. - /// The starting index of the output in the returned buffer. - /// The length of the output buffer. - byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength); - - /// - /// Filters the specified input, flushing all internally buffered data to the output. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes of the input to filter. - /// The starting index of the output in the returned buffer. - /// The length of the output buffer. - byte[] Flush (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength); - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - void Reset (); - } -} diff --git a/src/MimeKit/IO/Filters/MimeFilterBase.cs b/src/MimeKit/IO/Filters/MimeFilterBase.cs deleted file mode 100644 index 4f229b9..0000000 --- a/src/MimeKit/IO/Filters/MimeFilterBase.cs +++ /dev/null @@ -1,235 +0,0 @@ -// -// MimeFilterBase.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.IO.Filters { - /// - /// A base implementation for MIME filters. - /// - /// - /// A base implementation for MIME filters. - /// - public abstract class MimeFilterBase : IMimeFilter - { - int preloadLength; - byte[] preload; - byte[] output; - byte[] inbuf; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - protected MimeFilterBase () - { - } - - /// - /// Gets the output buffer. - /// - /// - /// Gets the output buffer. - /// - /// The output buffer. - protected byte[] OutputBuffer { - get { return output; } - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected abstract byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush); - - static int GetIdealBufferSize (int need) - { - return (need + 63) & ~63; - } - - byte[] PreFilter (byte[] input, ref int startIndex, ref int length) - { - if (preloadLength == 0) - return input; - - // We need to preload any data from a previous filter iteration into - // the input buffer, so make sure that we have room... - int totalLength = length + preloadLength; - - if (inbuf == null || inbuf.Length < totalLength) { - // NOTE: Array.Resize() copies data, we don't need that (slower) - inbuf = new byte[GetIdealBufferSize (totalLength)]; - } - - // Copy our preload data into our internal input buffer - Buffer.BlockCopy (preload, 0, inbuf, 0, preloadLength); - - // Copy our input to the end of our internal input buffer - Buffer.BlockCopy (input, startIndex, inbuf, preloadLength, length); - - length = totalLength; - preloadLength = 0; - startIndex = 0; - - return inbuf; - } - - static void ValidateArguments (byte[] input, int startIndex, int length) - { - if (input == null) - throw new ArgumentNullException (nameof (input)); - - if (startIndex < 0 || startIndex > input.Length) - throw new ArgumentOutOfRangeException (nameof (startIndex)); - - if (length < 0 || length > (input.Length - startIndex)) - throw new ArgumentOutOfRangeException (nameof (length)); - } - - /// - /// Filters the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes of the input to filter. - /// The starting index of the output in the returned buffer. - /// The length of the output buffer. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength) - { - ValidateArguments (input, startIndex, length); - - input = PreFilter (input, ref startIndex, ref length); - - return Filter (input, startIndex, length, out outputIndex, out outputLength, false); - } - - /// - /// Filters the specified input, flushing all internally buffered data to the output. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes of the input to filter. - /// The starting index of the output in the returned buffer. - /// The length of the output buffer. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public byte[] Flush (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength) - { - ValidateArguments (input, startIndex, length); - - input = PreFilter (input, ref startIndex, ref length); - - return Filter (input, startIndex, length, out outputIndex, out outputLength, true); - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public virtual void Reset () - { - preloadLength = 0; - } - - /// - /// Saves the remaining input for the next round of processing. - /// - /// - /// Saves the remaining input for the next round of processing. - /// - /// The input buffer. - /// The starting index of the buffer to save. - /// The length of the buffer to save, starting at . - protected void SaveRemainingInput (byte[] input, int startIndex, int length) - { - if (length == 0) - return; - - if (preload == null || preload.Length < length) - preload = new byte[GetIdealBufferSize (length)]; - - Buffer.BlockCopy (input, startIndex, preload, 0, length); - preloadLength = length; - } - - /// - /// Ensures that the output buffer is greater than or equal to the specified size. - /// - /// - /// Ensures that the output buffer is greater than or equal to the specified size. - /// - /// The minimum size needed. - /// If set to true, the current output should be preserved. - protected void EnsureOutputSize (int size, bool keep) - { - int outputSize = output != null ? output.Length : -1; - - if (outputSize >= size) - return; - - if (keep && output != null) - Array.Resize (ref output, GetIdealBufferSize (size)); - else - output = new byte[GetIdealBufferSize (size)]; - } - } -} diff --git a/src/MimeKit/IO/Filters/PassThroughFilter.cs b/src/MimeKit/IO/Filters/PassThroughFilter.cs deleted file mode 100644 index 61bc497..0000000 --- a/src/MimeKit/IO/Filters/PassThroughFilter.cs +++ /dev/null @@ -1,100 +0,0 @@ -// -// PassThroughFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.IO.Filters { - /// - /// A filter that simply passes data through without any processing. - /// - /// - /// Passes data through without any processing. - /// - public class PassThroughFilter : IMimeFilter - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public PassThroughFilter () - { - } - - #region IMimeFilter implementation - - /// - /// Filters the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes of the input to filter. - /// The starting index of the output in the returned buffer. - /// The length of the output buffer. - public byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength) - { - outputIndex = startIndex; - outputLength = length; - return input; - } - - /// - /// Filters the specified input, flushing all internally buffered data to the output. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes of the input to filter. - /// The starting index of the output in the returned buffer. - /// The length of the output buffer. - public byte[] Flush (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength) - { - outputIndex = startIndex; - outputLength = length; - return input; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public void Reset () - { - } - - #endregion - } -} diff --git a/src/MimeKit/IO/Filters/TrailingWhitespaceFilter.cs b/src/MimeKit/IO/Filters/TrailingWhitespaceFilter.cs deleted file mode 100644 index 08916e5..0000000 --- a/src/MimeKit/IO/Filters/TrailingWhitespaceFilter.cs +++ /dev/null @@ -1,140 +0,0 @@ -// -// TrailingWhitespaceFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using MimeKit.Utils; - -namespace MimeKit.IO.Filters { - /// - /// A filter for stripping trailing whitespace from lines in a textual stream. - /// - /// - /// Strips trailing whitespace from lines in a textual stream. - /// - public class TrailingWhitespaceFilter : MimeFilterBase - { - readonly PackedByteArray lwsp = new PackedByteArray (); - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public TrailingWhitespaceFilter () - { - } - - unsafe int Filter (byte* inbuf, int length, byte* outbuf) - { - byte* inend = inbuf + length; - byte* outptr = outbuf; - byte* inptr = inbuf; - int count = 0; - - while (inptr < inend) { - if ((*inptr).IsBlank ()) { - lwsp.Add (*inptr); - } else if (*inptr == (byte) '\r') { - *outptr++ = *inptr; - lwsp.Clear (); - count++; - } else if (*inptr == (byte) '\n') { - *outptr++ = *inptr; - lwsp.Clear (); - count++; - } else { - if (lwsp.Count > 0) { - lwsp.CopyTo (OutputBuffer, count); - outptr += lwsp.Count; - count += lwsp.Count; - lwsp.Clear (); - } - - *outptr++ = *inptr; - count++; - } - - inptr++; - } - - return count; - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - if (length == 0) { - if (flush) - lwsp.Clear (); - - outputIndex = startIndex; - outputLength = length; - - return input; - } - - EnsureOutputSize (length + lwsp.Count, false); - - unsafe { - fixed (byte* inptr = input, outptr = OutputBuffer) { - outputLength = Filter (inptr + startIndex, length, outptr); - } - } - - if (flush) - lwsp.Clear (); - - outputIndex = 0; - - return OutputBuffer; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - lwsp.Clear (); - base.Reset (); - } - } -} diff --git a/src/MimeKit/IO/Filters/Unix2DosFilter.cs b/src/MimeKit/IO/Filters/Unix2DosFilter.cs deleted file mode 100644 index d70f31e..0000000 --- a/src/MimeKit/IO/Filters/Unix2DosFilter.cs +++ /dev/null @@ -1,120 +0,0 @@ -// -// Unix2DosFilter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.IO.Filters { - /// - /// A filter that will convert from Unix line endings to Windows/DOS line endings. - /// - /// - /// Converts from Unix line endings to Windows/DOS line endings. - /// - public class Unix2DosFilter : MimeFilterBase - { - readonly bool ensureNewLine; - byte pc; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// Ensure that the stream ends with a new line. - public Unix2DosFilter (bool ensureNewLine = false) - { - this.ensureNewLine = ensureNewLine; - } - - unsafe int Filter (byte* inbuf, int length, byte* outbuf, bool flush) - { - byte* inend = inbuf + length; - byte* outptr = outbuf; - byte* inptr = inbuf; - - while (inptr < inend) { - if (*inptr == (byte) '\r') { - *outptr++ = *inptr; - } else if (*inptr == (byte) '\n') { - if (pc != (byte) '\r') - *outptr++ = (byte) '\r'; - *outptr++ = *inptr; - } else { - *outptr++ = *inptr; - } - - pc = *inptr++; - } - - if (flush && ensureNewLine && pc != (byte) '\n') { - *outptr++ = (byte) '\r'; - *outptr++ = (byte) '\n'; - } - - return (int) (outptr - outbuf); - } - - /// - /// Filter the specified input. - /// - /// - /// Filters the specified input buffer starting at the given index, - /// spanning across the specified number of bytes. - /// - /// The filtered output. - /// The input buffer. - /// The starting index of the input buffer. - /// The length of the input buffer, starting at . - /// The output index. - /// The output length. - /// If set to true, all internally buffered data should be flushed to the output buffer. - protected override byte[] Filter (byte[] input, int startIndex, int length, out int outputIndex, out int outputLength, bool flush) - { - EnsureOutputSize (length * 2 + (flush && ensureNewLine ? 2 : 0), false); - - outputIndex = 0; - - unsafe { - fixed (byte* inptr = input, outptr = OutputBuffer) { - outputLength = Filter (inptr + startIndex, length, outptr, flush); - } - } - - return OutputBuffer; - } - - /// - /// Resets the filter. - /// - /// - /// Resets the filter. - /// - public override void Reset () - { - pc = 0; - base.Reset (); - } - } -} diff --git a/src/MimeKit/IO/ICancellableStream.cs b/src/MimeKit/IO/ICancellableStream.cs deleted file mode 100644 index 2fa2c6d..0000000 --- a/src/MimeKit/IO/ICancellableStream.cs +++ /dev/null @@ -1,95 +0,0 @@ -// -// ICancellableStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Threading; - -namespace MimeKit.IO { - /// - /// An interface allowing for a cancellable stream reading operation. - /// - /// - /// This interface is meant to extend the functionality of a , - /// allowing the to have much finer-grained canellability. - /// When a custom stream implementation also implements this interface, - /// the will opt to use this interface - /// instead of the normal - /// API to read data from the stream. - /// This is really useful when parsing a message or other MIME entity - /// directly from a network-based stream. - /// - public interface ICancellableStream - { - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// When a custom stream implementation also implements this interface, - /// the will opt to use this interface - /// instead of the normal - /// API to read data from the stream. - /// This is really useful when parsing a message or other MIME entity - /// directly from a network-based stream. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many - /// bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// The cancellation token. - int Read (byte[] buffer, int offset, int count, CancellationToken cancellationToken); - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// When a custom stream implementation also implements this interface, - /// writing a or - /// to the custom stream will opt to use this interface - /// instead of the normal - /// API to write data to the stream. - /// This is really useful when writing a message or other MIME entity - /// directly to a network-based stream. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token - void Write (byte[] buffer, int offset, int count, CancellationToken cancellationToken); - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// The cancellation token. - void Flush (CancellationToken cancellationToken); - } -} diff --git a/src/MimeKit/IO/MeasuringStream.cs b/src/MimeKit/IO/MeasuringStream.cs deleted file mode 100644 index 6ce1c70..0000000 --- a/src/MimeKit/IO/MeasuringStream.cs +++ /dev/null @@ -1,427 +0,0 @@ -// -// MeasuringStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MimeKit.IO { - /// - /// A stream useful for measuring the amount of data written. - /// - /// - /// A keeps track of the number of bytes - /// that have been written to it. This is useful, for example, when you - /// need to know how large a is without - /// actually writing it to disk or into a memory buffer. - /// - public class MeasuringStream : Stream - { - bool disposed; - long position; - long length; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public MeasuringStream () - { - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (MeasuringStream)); - } - - #region implemented abstract members of Stream - - /// - /// Checks whether or not the stream supports reading. - /// - /// - /// A is not readable. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return false; } - } - - /// - /// Checks whether or not the stream supports writing. - /// - /// - /// A is always writable. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return true; } - } - - /// - /// Checks whether or not the stream supports seeking. - /// - /// - /// A is always seekable. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return true; } - } - - /// - /// Checks whether or not reading and writing to the stream can timeout. - /// - /// - /// Writing to a cannot timeout. - /// - /// true if reading and writing to the stream can timeout; otherwise, false. - public override bool CanTimeout { - get { return false; } - } - - /// - /// Gets the length of the stream, in bytes. - /// - /// - /// The length of a indicates the - /// number of bytes that have been written to it. - /// - /// The length of the stream in bytes. - /// - /// The stream has been disposed. - /// - public override long Length { - get { - CheckDisposed (); - - return length; - } - } - - /// - /// Gets or sets the current position within the stream. - /// - /// - /// Since it is possible to seek within a , - /// it is possible that the position will not always be identical to the - /// length of the stream, but typically it will be. - /// - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return position; } - set { Seek (value, SeekOrigin.Begin); } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reading from a is not supported. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - public override int Read (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - throw new NotSupportedException ("The stream does not support reading"); - } - - /// - /// Asynchronously reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reading from a is not supported. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support reading. - /// - public override Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - CheckDisposed (); - - throw new NotSupportedException ("The stream does not support reading"); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Increments the property by the number of bytes written. - /// If the updated position is greater than the current length of the stream, then - /// the property will be updated to be identical to the - /// position. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - position += count; - - length = Math.Max (length, position); - } - - /// - /// Asynchronously writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Increments the property by the number of bytes written. - /// If the updated position is greater than the current length of the stream, then - /// the property will be updated to be identical to the - /// position. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - Write (buffer, offset, count); - - return Task.FromResult (0); - } - - /// - /// Sets the position within the current stream. - /// - /// - /// Updates the within the stream. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// is not a valid . - /// - /// - /// The stream has been disposed. - /// - public override long Seek (long offset, SeekOrigin origin) - { - long real; - - CheckDisposed (); - - switch (origin) { - case SeekOrigin.Begin: - real = offset; - break; - case SeekOrigin.Current: - real = position + offset; - break; - case SeekOrigin.End: - real = length + offset; - break; - default: - throw new ArgumentOutOfRangeException (nameof (origin), "Invalid SeekOrigin specified"); - } - - // sanity check the resultant offset - if (real < 0) - throw new IOException ("Cannot seek to a position before the beginning of the stream"); - - // short-cut if we are seeking to our current position - if (real == position) - return position; - - if (real > length) - throw new IOException ("Cannot seek beyond the end of the stream"); - - position = real; - - return position; - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Since a does not actually do anything other than - /// count bytes, this method is a no-op. - /// - /// - /// The stream has been disposed. - /// - public override void Flush () - { - CheckDisposed (); - - // nothing to do... - } - - /// - /// Asynchronously clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// Since a does not actually do anything other than - /// count bytes, this method is a no-op. - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - public override Task FlushAsync (CancellationToken cancellationToken) - { - CheckDisposed (); - - // nothing to do... - return Task.FromResult (0); - } - - /// - /// Sets the length of the stream. - /// - /// - /// Sets the to the specified value and updates - /// to the specified value if (and only if) - /// the current position is greater than the new length value. - /// - /// The desired length of the stream in bytes. - /// - /// is out of range. - /// - /// - /// The stream has been disposed. - /// - public override void SetLength (long value) - { - CheckDisposed (); - - if (value < 0) - throw new ArgumentOutOfRangeException (nameof (value)); - - position = Math.Min (position, value); - length = value; - } - - #endregion - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - base.Dispose (disposing); - disposed = true; - } - } -} diff --git a/src/MimeKit/IO/MemoryBlockStream.cs b/src/MimeKit/IO/MemoryBlockStream.cs deleted file mode 100644 index 0f3e6a0..0000000 --- a/src/MimeKit/IO/MemoryBlockStream.cs +++ /dev/null @@ -1,557 +0,0 @@ -// -// MemoryBlockStream.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Diagnostics; - -using MimeKit.Utils; - -namespace MimeKit.IO { - /// - /// An efficient memory stream implementation that sacrifices the ability to - /// get access to the internal byte buffer in order to drastically improve - /// performance. - /// - /// - /// Instead of resizing an internal byte array, the - /// chains blocks of non-contiguous memory. This helps improve performance by avoiding - /// unneeded copying of data from the old array to the newly allocated array as well - /// as the zeroing of the newly allocated array. - /// - public class MemoryBlockStream : Stream - { - const long MaxCapacity = int.MaxValue * BlockSize; - const long BlockSize = 2048; - - static readonly BufferPool DefaultPool = new BufferPool ((int) BlockSize, 200); - - readonly List blocks = new List (); - readonly BufferPool pool; - long position, length; - bool disposed; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with an initial memory block - /// of 2048 bytes. - /// - public MemoryBlockStream () - { - pool = DefaultPool; - blocks.Add (pool.Rent (Debugger.IsAttached)); - } - - /// - /// Copies the memory stream into a byte array. - /// - /// - /// Copies all of the stream data into a newly allocated byte array. - /// - /// The array. - public byte[] ToArray () - { - var array = new byte[length]; - int need = (int) length; - int arrayIndex = 0; - int nread = 0; - int block = 0; - - while (nread < length) { - int n = Math.Min ((int) BlockSize, need); - Buffer.BlockCopy (blocks[block], 0, array, arrayIndex, n); - arrayIndex += n; - nread += n; - need -= n; - block++; - } - - return array; - } - - void CheckDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (MemoryBlockStream)); - } - - #region implemented abstract members of Stream - - /// - /// Checks whether or not the stream supports reading. - /// - /// - /// The is always readable. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead { - get { return true; } - } - - /// - /// Checks whether or not the stream supports writing. - /// - /// - /// The is always writable. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite { - get { return true; } - } - - /// - /// Checks whether or not the stream supports seeking. - /// - /// - /// The is always seekable. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek { - get { return true; } - } - - /// - /// Checks whether or not reading and writing to the stream can timeout. - /// - /// - /// The does not support timing out. - /// - /// true if reading and writing to the stream can timeout; otherwise, false. - public override bool CanTimeout { - get { return false; } - } - - /// - /// Gets the length of the stream, in bytes. - /// - /// - /// Gets the length of the stream, in bytes. - /// - /// The length of the stream, in bytes. - /// - /// The stream has been disposed. - /// - public override long Length { - get { - CheckDisposed (); - - return length; - } - } - - /// - /// Gets or sets the current position within the stream. - /// - /// - /// Gets or sets the current position within the stream. - /// - /// The position of the stream. - /// - /// An I/O error occurred. - /// - /// - /// The stream does not support seeking. - /// - /// - /// The stream has been disposed. - /// - public override long Position { - get { return position; } - set { Seek (value, SeekOrigin.Begin); } - } - - static void ValidateArguments (byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException (nameof (buffer)); - - if (offset < 0 || offset > buffer.Length) - throw new ArgumentOutOfRangeException (nameof (offset)); - - if (count < 0 || count > (buffer.Length - offset)) - throw new ArgumentOutOfRangeException (nameof (count)); - } - - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// An I/O error occurred. - /// - public override int Read (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - if (position == MaxCapacity) - return 0; - - int max = Math.Min ((int) (length - position), count); - int startIndex = (int) (position % BlockSize); - int block = (int) (position / BlockSize); - int nread = 0; - - while (nread < max && block < blocks.Count) { - int n = Math.Min ((int) BlockSize - startIndex, max - nread); - Buffer.BlockCopy (blocks[block], startIndex, buffer, offset + nread, n); - startIndex = 0; - nread += n; - block++; - } - - position += nread; - - return nread; - } - - /// - /// Asynchronously reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// - /// Reads a sequence of bytes from the stream and advances the position - /// within the stream by the number of bytes read. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if - /// that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// The buffer to read data into. - /// The offset into the buffer to start reading data. - /// The number of bytes to read. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// An I/O error occurred. - /// - public override Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return Task.FromResult (Read (buffer, offset, count)); - } - - /// - /// Writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes the entire buffer to the stream and advances the current position - /// within the stream by the number of bytes written, adding memory blocks as - /// needed in order to contain the newly written bytes. - /// - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override void Write (byte[] buffer, int offset, int count) - { - CheckDisposed (); - - ValidateArguments (buffer, offset, count); - - if (position + count >= MaxCapacity) - throw new IOException (string.Format ("Cannot exceed {0} bytes", MaxCapacity)); - - int startIndex = (int) (position % BlockSize); - long capacity = blocks.Count * BlockSize; - int block = (int) (position / BlockSize); - int nwritten = 0; - - while (capacity < position + count) { - blocks.Add (pool.Rent (Debugger.IsAttached)); - capacity += BlockSize; - } - - while (nwritten < count) { - int n = Math.Min ((int) BlockSize - startIndex, count - nwritten); - Buffer.BlockCopy (buffer, offset + nwritten, blocks[block], startIndex, n); - startIndex = 0; - nwritten += n; - block++; - } - - position += nwritten; - - length = Math.Max (length, position); - } - - /// - /// Asynchronously writes a sequence of bytes to the stream and advances the current - /// position within this stream by the number of bytes written. - /// - /// - /// Writes the entire buffer to the stream and advances the current position - /// within the stream by the number of bytes written, adding memory blocks as - /// needed in order to contain the newly written bytes. - /// - /// A task that represents the asynchronous write operation. - /// The buffer to write. - /// The offset of the first byte to write. - /// The number of bytes to write. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is less than zero or greater than the length of . - /// -or- - /// The is not large enough to contain bytes starting - /// at the specified . - /// - /// - /// The stream has been disposed. - /// - /// - /// The stream does not support writing. - /// - /// - /// An I/O error occurred. - /// - public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - Write (buffer, offset, count); - - return Task.FromResult (0); - } - - /// - /// Sets the position within the current stream. - /// - /// - /// Sets the position within the current stream. - /// - /// The new position within the stream. - /// The offset into the stream relative to the . - /// The origin to seek from. - /// - /// is not a valid . - /// - /// - /// The stream has been disposed. - /// - /// - /// An I/O error occurred. - /// - public override long Seek (long offset, SeekOrigin origin) - { - long real; - - CheckDisposed (); - - switch (origin) { - case SeekOrigin.Begin: - real = offset; - break; - case SeekOrigin.Current: - real = position + offset; - break; - case SeekOrigin.End: - real = length + offset; - break; - default: - throw new ArgumentOutOfRangeException (nameof (origin), "Invalid SeekOrigin specified"); - } - - // sanity check the resultant offset - if (real < 0) - throw new IOException ("Cannot seek to a position before the beginning of the stream"); - - if (real > MaxCapacity) - throw new IOException (string.Format ("Cannot exceed {0} bytes", MaxCapacity)); - - // short-cut if we are seeking to our current position - if (real == position) - return position; - - // TODO: MemoryStream allows seeking past the end - should MemoryBlockStream? - if (real > length) - throw new IOException ("Cannot seek beyond the end of the stream"); - - position = real; - - return position; - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// This method does not do anything. - /// - /// - /// The stream has been disposed. - /// - public override void Flush () - { - CheckDisposed (); - - // nothing to do... - } - - /// - /// Asynchronously clears all buffers for this stream and causes any buffered data to be written - /// to the underlying device. - /// - /// - /// This method does not do anything. - /// - /// A task that represents the asynchronous flush operation. - /// The cancellation token. - /// - /// The stream has been disposed. - /// - public override Task FlushAsync (CancellationToken cancellationToken) - { - CheckDisposed (); - - return Task.FromResult (0); - } - - /// - /// Sets the length of the stream. - /// - /// - /// Sets the length of the stream. - /// - /// The desired length of the stream in bytes. - /// - /// is out of range. - /// - /// - /// The stream has been disposed. - /// - public override void SetLength (long value) - { - CheckDisposed (); - - if (value < 0 || value > MaxCapacity) - throw new ArgumentOutOfRangeException (nameof (value)); - - long capacity = blocks.Count * BlockSize; - - if (value > capacity) { - do { - blocks.Add (pool.Rent (Debugger.IsAttached)); - capacity += BlockSize; - } while (capacity < value); - } else if (value < length) { - // shed any blocks that are no longer needed - while (capacity - value > BlockSize) { - pool.Return (blocks[blocks.Count - 1]); - blocks.RemoveAt (blocks.Count - 1); - capacity -= BlockSize; - } - - // reset the range of bytes between the new length and the old length to 0 - int count = (int) (Math.Min (length, capacity) - value); - int startIndex = (int) (value % BlockSize); - int block = (int) (value / BlockSize); - - Array.Clear (blocks[block], startIndex, count); - } - - position = Math.Min (position, value); - length = value; - } - - #endregion - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected override void Dispose (bool disposing) - { - if (disposing && !disposed) { - for (int i = 0; i < blocks.Count; i++) { - pool.Return (blocks[i]); - blocks[i] = null; - } - - blocks.Clear (); - disposed = true; - } - - base.Dispose (disposing); - } - } -} diff --git a/src/MimeKit/InternetAddress.cs b/src/MimeKit/InternetAddress.cs deleted file mode 100644 index d71fc2b..0000000 --- a/src/MimeKit/InternetAddress.cs +++ /dev/null @@ -1,1361 +0,0 @@ -// -// InternetAddress.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// An abstract internet address, as specified by rfc0822. - /// - /// - /// A can be any type of address defined by the - /// original Internet Message specification. - /// There are effectively two (2) types of addresses: mailboxes and groups. - /// Mailbox addresses are what are most commonly known as email addresses and are - /// represented by the class. - /// Group addresses are themselves lists of addresses and are represented by the - /// class. While rare, it is still important to handle these - /// types of addresses. They typically only contain mailbox addresses, but may also - /// contain other group addresses. - /// - public abstract class InternetAddress : IComparable, IEquatable - { - const string AtomSpecials = "()<>@,;:\\\".[]"; - Encoding encoding; - string name; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Initializes the and properties of the internet address. - /// - /// The character encoding to be used for encoding the name. - /// The name of the mailbox or group. - /// - /// is null. - /// - protected InternetAddress (Encoding encoding, string name) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - Encoding = encoding; - Name = name; - } - - /// - /// Get or set the character encoding to use when encoding the name of the address. - /// - /// - /// The character encoding is used to convert the property, if it is set, - /// to a stream of bytes when encoding the internet address for transport. - /// - /// The character encoding. - /// - /// is null. - /// - public Encoding Encoding { - get { return encoding; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (value == encoding) - return; - - encoding = value; - OnChanged (); - } - } - - /// - /// Get or set the display name of the address. - /// - /// - /// A name is optional and is typically set to the name of the person - /// or group that own the internet address. - /// - /// The name of the address. - public string Name { - get { return name; } - set { - if (value == name) - return; - - name = value; - OnChanged (); - } - } - - /// - /// Clone the address. - /// - /// - /// Clones the address. - /// - /// The cloned address. - public abstract InternetAddress Clone (); - - #region IComparable implementation - - /// - /// Compares two internet addresses. - /// - /// - /// Compares two internet addresses for the purpose of sorting. - /// - /// The sort order of the current internet address compared to the other internet address. - /// The internet address to compare to. - /// - /// is null. - /// - public int CompareTo (InternetAddress other) - { - int rv; - - if (other == null) - throw new ArgumentNullException (nameof (other)); - - if ((rv = string.Compare (Name, other.Name, StringComparison.OrdinalIgnoreCase)) != 0) - return rv; - - var otherMailbox = other as MailboxAddress; - var mailbox = this as MailboxAddress; - - if (mailbox != null && otherMailbox != null) { - string otherAddress = otherMailbox.Address; - int otherAt = otherAddress.IndexOf ('@'); - string address = mailbox.Address; - int at = address.IndexOf ('@'); - - if (at != -1 && otherAt != -1) { - int length = Math.Min (address.Length - (at + 1), otherAddress.Length - (otherAt + 1)); - - rv = string.Compare (address, at + 1, otherAddress, otherAt + 1, length, StringComparison.OrdinalIgnoreCase); - } - - if (rv == 0) { - string otherUser = otherAt != -1 ? otherAddress.Substring (0, otherAt) : otherAddress; - string user = at != -1 ? address.Substring (0, at) : address; - - rv = string.Compare (user, otherUser, StringComparison.OrdinalIgnoreCase); - } - - return rv; - } - - // sort mailbox addresses before group addresses - if (mailbox != null && otherMailbox == null) - return -1; - - if (mailbox == null && otherMailbox != null) - return 1; - - return 0; - } - - #endregion - - #region IEquatable implementation - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Compares two internet addresses to determine if they are identical or not. - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public abstract bool Equals (InternetAddress other); - - #endregion - - /// - /// Determine whether the specified object is equal to the current object. - /// - /// - /// The type of comparison between the current instance and the parameter depends on whether - /// the current instance is a reference type or a value type. - /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals (object obj) - { - return Equals (obj as InternetAddress); - } - - /// - /// Return the hash code for this instance. - /// - /// - /// Returns the hash code for this instance. - /// - /// A hash code for the current object. - public override int GetHashCode () - { - return ToString ().GetHashCode (); - } - - internal static string EncodeInternationalizedPhrase (string phrase) - { - for (int i = 0; i < phrase.Length; i++) { - if (AtomSpecials.IndexOf (phrase[i]) != -1) - return MimeUtils.Quote (phrase); - } - - return phrase; - } - - internal abstract void Encode (FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength); - - /// - /// Serialize an to a string, optionally encoding it for transport. - /// - /// - /// If the parameter is true, then this method will return - /// an encoded version of the internet address according to the rules described in rfc2047. - /// However, if the parameter is false, then this method will - /// return a string suitable only for display purposes. - /// - /// A string representing the . - /// The formatting options. - /// If set to true, the will be encoded. - /// - /// is null. - /// - public abstract string ToString (FormatOptions options, bool encode); - - /// - /// Serialize an to a string, optionally encoding it for transport. - /// - /// - /// If the parameter is true, then this method will return - /// an encoded version of the internet address according to the rules described in rfc2047. - /// However, if the parameter is false, then this method will - /// return a string suitable only for display purposes. - /// - /// A string representing the . - /// If set to true, the will be encoded. - public string ToString (bool encode) - { - return ToString (FormatOptions.Default, encode); - } - - /// - /// Serialize an to a string suitable for display. - /// - /// - /// The string returned by this method is suitable only for display purposes. - /// - /// A string representing the . - public override string ToString () - { - return ToString (FormatOptions.Default, false); - } - - internal event EventHandler Changed; - - /// - /// Raise the internal changed event used by to keep headers in sync. - /// - /// - /// This method is called whenever a property of the internet address is changed. - /// - protected virtual void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - - internal static bool TryParseLocalPart (byte[] text, ref int index, int endIndex, bool skipTrailingCfws, bool throwOnError, out string localpart) - { - var token = new StringBuilder (); - int startIndex = index; - - localpart = null; - - do { - if (!text[index].IsAtom () && text[index] != '"') { - if (throwOnError) - throw new ParseException (string.Format ("Invalid local-part at offset {0}", startIndex), startIndex, index); - - return false; - } - - int start = index; - if (!ParseUtils.SkipWord (text, ref index, endIndex, throwOnError)) - return false; - - try { - token.Append (CharsetUtils.UTF8.GetString (text, start, index - start)); - } catch (DecoderFallbackException) { - try { - token.Append (CharsetUtils.Latin1.GetString (text, start, index - start)); - } catch (DecoderFallbackException ex) { - if (throwOnError) - throw new ParseException ("Internationalized local-part tokens may only contain UTF-8 characters.", start, start, ex); - - return false; - } - } - - int cfws = index; - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex || text[index] != (byte) '.') { - if (!skipTrailingCfws) - index = cfws; - break; - } - - token.Append ('.'); - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete local-part at offset {0}", startIndex), startIndex, index); - - return false; - } - } while (true); - - localpart = token.ToString (); - - if (ParseUtils.IsIdnEncoded (localpart)) - localpart = ParseUtils.IdnDecode (localpart); - - return true; - } - - static readonly byte[] CommaGreaterThanOrSemiColon = { (byte) ',', (byte) '>', (byte) ';' }; - - internal static bool TryParseAddrspec (byte[] text, ref int index, int endIndex, byte[] sentinels, bool throwOnError, out string addrspec, out int at) - { - int startIndex = index; - string localpart; - - addrspec = null; - at = -1; - - if (!TryParseLocalPart (text, ref index, endIndex, true, throwOnError, out localpart)) - return false; - - if (index >= endIndex || ParseUtils.IsSentinel (text[index], sentinels)) { - addrspec = localpart; - return true; - } - - if (text[index] != (byte) '@') { - if (throwOnError) - throw new ParseException (string.Format ("Invalid addr-spec token at offset {0}", startIndex), startIndex, index); - - return false; - } - - index++; - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete addr-spec token at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete addr-spec token at offset {0}", startIndex), startIndex, index); - - return false; - } - - string domain; - if (!ParseUtils.TryParseDomain (text, ref index, endIndex, sentinels, throwOnError, out domain)) - return false; - - if (ParseUtils.IsIdnEncoded (domain)) - domain = ParseUtils.IdnDecode (domain); - - addrspec = localpart + "@" + domain; - at = localpart.Length; - - return true; - } - - internal static bool TryParseMailbox (ParserOptions options, byte[] text, int startIndex, ref int index, int endIndex, string name, int codepage, bool throwOnError, out InternetAddress address) - { - DomainList route = null; - Encoding encoding; - - try { - encoding = Encoding.GetEncoding (codepage); - } catch { - encoding = Encoding.UTF8; - } - - address = null; - - // skip over the '<' - index++; - - // Note: check for excessive angle brackets like the example described in section 7.1.2 of rfc7103... - if (index < endIndex && text[index] == (byte) '<') { - if (options.AddressParserComplianceMode == RfcComplianceMode.Strict) { - if (throwOnError) - throw new ParseException (string.Format ("Excessive angle brackets at offset {0}", index), startIndex, index); - - return false; - } - - do { - index++; - } while (index < endIndex && text[index] == '<'); - } - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete mailbox at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (text[index] == (byte) '@') { - // Note: we always pass 'false' as the throwOnError argument here so that we can throw a more informative exception on error - if (!DomainList.TryParse (text, ref index, endIndex, false, out route)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid route in mailbox at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (index >= endIndex || text[index] != (byte) ':') { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete route in mailbox at offset {0}", startIndex), startIndex, index); - - return false; - } - - // skip over ':' - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete mailbox at offset {0}", startIndex), startIndex, index); - - return false; - } - } - - // Note: The only syntactically correct sentinel token here is the '>', but alas... to deal with the first example - // in section 7.1.5 of rfc7103, we need to at least handle ',' as a sentinel and might as well handle ';' as well - // in case the mailbox is within a group address. - // - // Example: - string addrspec; - int at; - - if (!TryParseAddrspec (text, ref index, endIndex, CommaGreaterThanOrSemiColon, throwOnError, out addrspec, out at)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex || text[index] != (byte) '>') { - if (options.AddressParserComplianceMode == RfcComplianceMode.Strict) { - if (throwOnError) - throw new ParseException (string.Format ("Unexpected end of mailbox at offset {0}", startIndex), startIndex, index); - - return false; - } - } else { - // skip over the '>' - index++; - - // Note: check for excessive angle brackets like the example described in section 7.1.2 of rfc7103... - if (index < endIndex && text[index] == (byte) '>') { - if (options.AddressParserComplianceMode == RfcComplianceMode.Strict) { - if (throwOnError) - throw new ParseException (string.Format ("Excessive angle brackets at offset {0}", index), startIndex, index); - - return false; - } - - do { - index++; - } while (index < endIndex && text[index] == '>'); - } - } - - if (route != null) - address = new MailboxAddress (encoding, name, route, addrspec, at); - else - address = new MailboxAddress (encoding, name, addrspec, at); - - return true; - } - - static bool TryParseGroup (ParserOptions options, byte[] text, int startIndex, ref int index, int endIndex, int groupDepth, string name, int codepage, bool throwOnError, out InternetAddress address) - { - List members; - Encoding encoding; - - try { - encoding = Encoding.GetEncoding (codepage); - } catch { - encoding = Encoding.UTF8; - } - - address = null; - - // skip over the ':' - index++; - - while (index < endIndex && (text[index] == ':' || text[index].IsBlank ())) - index++; - - if (InternetAddressList.TryParse (options, text, ref index, endIndex, true, groupDepth, throwOnError, out members)) - address = new GroupAddress (encoding, name, members); - else - address = new GroupAddress (encoding, name); - - if (index >= endIndex || text[index] != (byte) ';') { - if (throwOnError && options.AddressParserComplianceMode == RfcComplianceMode.Strict) - throw new ParseException (string.Format ("Expected to find ';' at offset {0}", index), startIndex, index); - - while (index < endIndex && text[index] != (byte) ';') - index++; - } else { - index++; - } - - return true; - } - - [Flags] - internal enum AddressParserFlags { - AllowMailboxAddress = 1 << 0, - AllowGroupAddress = 1 << 1, - ThrowOnError = 1 << 2, - - TryParse = AllowMailboxAddress | AllowGroupAddress, - Parse = TryParse | ThrowOnError - } - - internal static bool TryParse (ParserOptions options, byte[] text, ref int index, int endIndex, int groupDepth, AddressParserFlags flags, out InternetAddress address) - { - bool strict = options.AddressParserComplianceMode == RfcComplianceMode.Strict; - bool throwOnError = (flags & AddressParserFlags.ThrowOnError) != 0; - int minWordCount = options.AllowUnquotedCommasInAddresses ? 0 : 1; - - address = null; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index == endIndex) { - if (throwOnError) - throw new ParseException ("No address found.", index, index); - - return false; - } - - // keep track of the start & length of the phrase - bool trimLeadingQuote = false; - int startIndex = index; - int length = 0; - int words = 0; - - while (index < endIndex) { - if (strict) { - if (!ParseUtils.SkipWord (text, ref index, endIndex, throwOnError)) - break; - } else if (text[index] == (byte) '"') { - int qstringIndex = index; - - if (!ParseUtils.SkipQuoted (text, ref index, endIndex, false)) { - index = qstringIndex + 1; - - ParseUtils.SkipWhiteSpace (text, ref index, endIndex); - - if (!ParseUtils.SkipPhraseAtom (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete quoted-string token at offset {0}", qstringIndex), qstringIndex, endIndex); - - break; - } - - if (startIndex == qstringIndex) - trimLeadingQuote = true; - } - } else { - if (!ParseUtils.SkipPhraseAtom (text, ref index, endIndex)) - break; - } - - length = index - startIndex; - - do { - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - // Note: some clients don't quote dots in the name - if (index >= endIndex || text[index] != (byte) '.') - break; - - index++; - - length = index - startIndex; - } while (true); - - words++; - - // Note: some clients don't quote commas in the name - if (index < endIndex && text[index] == ',' && words > minWordCount) { - index++; - - length = index - startIndex; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - } - } - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - // specials = "(" / ")" / "<" / ">" / "@" ; Must be in quoted- - // / "," / ";" / ":" / "\" / <"> ; string, to use - // / "." / "[" / "]" ; within a word. - - if (index >= endIndex || text[index] == (byte) ',' || text[index] == (byte) '>' || text[index] == ';') { - // we've completely gobbled up an addr-spec w/o a domain - byte sentinel = index < endIndex ? text[index] : (byte) ','; - string name, addrspec; - - if ((flags & AddressParserFlags.AllowMailboxAddress) == 0) { - if (throwOnError) - throw new ParseException (string.Format ("Addr-spec token at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (!options.AllowAddressesWithoutDomain) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete addr-spec token at offset {0}", startIndex), startIndex, index); - - return false; - } - - // rewind back to the beginning of the local-part - index = startIndex; - - if (!TryParseLocalPart (text, ref index, endIndex, false, throwOnError, out addrspec)) - return false; - - ParseUtils.SkipWhiteSpace (text, ref index, endIndex); - - if (index < endIndex && text[index] == '(') { - int comment = index + 1; - - // Note: this can't fail because it has already been skipped in TryParseLocalPart() above. - ParseUtils.SkipComment (text, ref index, endIndex); - - name = Rfc2047.DecodePhrase (options, text, comment, (index - 1) - comment).Trim (); - - ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError); - } else { - name = string.Empty; - } - - if (index < endIndex && text[index] == (byte) '>') { - if (strict) { - if (throwOnError) - throw new ParseException (string.Format ("Unexpected '>' token at offset {0}", index), startIndex, index); - - return false; - } - - index++; - } - - if (index < endIndex && text[index] != sentinel) { - if (throwOnError) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), startIndex, index); - - return false; - } - - address = new MailboxAddress (Encoding.UTF8, name, addrspec, -1); - - return true; - } - - if (text[index] == (byte) ':') { - // rfc2822 group address - int nameIndex = startIndex; - int codepage = -1; - string name; - - if ((flags & AddressParserFlags.AllowGroupAddress) == 0) { - if (throwOnError) - throw new ParseException (string.Format ("Group address token at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (groupDepth >= options.MaxAddressGroupDepth) { - if (throwOnError) - throw new ParseException (string.Format ("Exceeded maximum rfc822 group depth at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (trimLeadingQuote) { - nameIndex++; - length--; - } - - if (length > 0) { - name = Rfc2047.DecodePhrase (options, text, nameIndex, length, out codepage); - } else { - name = string.Empty; - } - - if (codepage == -1) - codepage = 65001; - - return TryParseGroup (options, text, startIndex, ref index, endIndex, groupDepth + 1, MimeUtils.Unquote (name), codepage, throwOnError, out address); - } - - if ((flags & AddressParserFlags.AllowMailboxAddress) == 0) { - if (throwOnError) - throw new ParseException (string.Format ("Mailbox address token at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (text[index] == (byte) '@') { - // we're either in the middle of an addr-spec token or we completely gobbled up an addr-spec w/o a domain - string name, addrspec; - int at; - - // rewind back to the beginning of the local-part - index = startIndex; - - if (!TryParseAddrspec (text, ref index, endIndex, CommaGreaterThanOrSemiColon, throwOnError, out addrspec, out at)) - return false; - - ParseUtils.SkipWhiteSpace (text, ref index, endIndex); - - if (index < endIndex && text[index] == '(') { - int comment = index; - - if (!ParseUtils.SkipComment (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete comment token at offset {0}", comment), comment, index); - - return false; - } - - comment++; - - name = Rfc2047.DecodePhrase (options, text, comment, (index - 1) - comment).Trim (); - } else { - name = string.Empty; - } - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - address = new MailboxAddress (Encoding.UTF8, name, addrspec, at); - return true; - } - - if (text[index] == (byte) '<') { - // We have an address like "user@example.com "; i.e. the name is an unquoted string with an '@'. - if (strict) { - if (throwOnError) - throw new ParseException (string.Format ("Unexpected '<' token at offset {0}", index), startIndex, index); - - return false; - } - - int nameEndIndex = index; - while (nameEndIndex > startIndex && text[nameEndIndex - 1].IsWhitespace ()) - nameEndIndex--; - - length = nameEndIndex - startIndex; - - // fall through to the rfc822 angle-addr token case... - } else { - // Note: since there was no '<', there should not be a '>'... but we handle it anyway in order to - // deal with the second Unbalanced Angle Brackets example in section 7.1.3: second@example.org> - if (text[index] == (byte) '>') { - if (strict) { - if (throwOnError) - throw new ParseException (string.Format ("Unexpected '>' token at offset {0}", index), startIndex, index); - - return false; - } - - index++; - } - - address = new MailboxAddress (Encoding.UTF8, name, addrspec, at); - - return true; - } - } - - if (text[index] == (byte) '<') { - // rfc2822 angle-addr token - int nameIndex = startIndex; - int codepage = -1; - string name; - - if (trimLeadingQuote) { - nameIndex++; - length--; - } - - if (length > 0) { - name = Rfc2047.DecodePhrase (options, text, nameIndex, length, out codepage); - } else { - name = string.Empty; - } - - if (codepage == -1) - codepage = 65001; - - return TryParseMailbox (options, text, startIndex, ref index, endIndex, MimeUtils.Unquote (name), codepage, throwOnError, out address); - } - - if (throwOnError) - throw new ParseException (string.Format ("Invalid address token at offset {0}", startIndex), startIndex, index); - - return false; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed address. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out InternetAddress address) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int endIndex = startIndex + length; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, 0, AddressParserFlags.TryParse, out address)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false)) { - address = null; - return false; - } - - if (index != endIndex) { - address = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed address. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out InternetAddress address) - { - return TryParse (ParserOptions.Default, buffer, startIndex, length, out address); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed address. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, out InternetAddress address) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int endIndex = buffer.Length; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, 0, AddressParserFlags.TryParse, out address)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - address = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed address. - /// - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (byte[] buffer, int startIndex, out InternetAddress address) - { - return TryParse (ParserOptions.Default, buffer, startIndex, out address); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The parsed address. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, out InternetAddress address) - { - ParseUtils.ValidateArguments (options, buffer); - - int endIndex = buffer.Length; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, 0, AddressParserFlags.TryParse, out address)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - address = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The parsed address. - /// - /// is null. - /// - public static bool TryParse (byte[] buffer, out InternetAddress address) - { - return TryParse (ParserOptions.Default, buffer, out address); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a single or . If the text contains - /// more data, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The text. - /// The parsed address. - /// - /// is null. - /// - public static bool TryParse (ParserOptions options, string text, out InternetAddress address) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - int endIndex = buffer.Length; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, 0, AddressParserFlags.TryParse, out address)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - address = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a single or . If the text contains - /// more data, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The text. - /// The parsed address. - /// - /// is null. - /// - public static bool TryParse (string text, out InternetAddress address) - { - return TryParse (ParserOptions.Default, text, out address); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// could not be parsed. - /// - public static InternetAddress Parse (ParserOptions options, byte[] buffer, int startIndex, int length) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int endIndex = startIndex + length; - InternetAddress address; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, 0, AddressParserFlags.Parse, out address)) - throw new ParseException ("No address found.", startIndex, startIndex); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return address; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// could not be parsed. - /// - public static InternetAddress Parse (byte[] buffer, int startIndex, int length) - { - return Parse (ParserOptions.Default, buffer, startIndex, length); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// could not be parsed. - /// - public static InternetAddress Parse (ParserOptions options, byte[] buffer, int startIndex) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int endIndex = buffer.Length; - InternetAddress address; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, 0, AddressParserFlags.Parse, out address)) - throw new ParseException ("No address found.", startIndex, startIndex); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return address; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// The starting index of the input buffer. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// could not be parsed. - /// - public static InternetAddress Parse (byte[] buffer, int startIndex) - { - return Parse (ParserOptions.Default, buffer, startIndex); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// could not be parsed. - /// - public static InternetAddress Parse (ParserOptions options, byte[] buffer) - { - ParseUtils.ValidateArguments (options, buffer); - - int endIndex = buffer.Length; - InternetAddress address; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, 0, AddressParserFlags.Parse, out address)) - throw new ParseException ("No address found.", 0, 0); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return address; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single or . If the buffer contains - /// more data, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public static InternetAddress Parse (byte[] buffer) - { - return Parse (ParserOptions.Default, buffer); - } - - /// - /// Parse the given text into a new instance. - /// - /// - /// Parses a single or . If the text contains - /// more data, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The text. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// could not be parsed. - /// - public static InternetAddress Parse (ParserOptions options, string text) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - int endIndex = buffer.Length; - InternetAddress address; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, 0, AddressParserFlags.Parse, out address)) - throw new ParseException ("No address found.", 0, 0); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return address; - } - - /// - /// Parse the given text into a new instance. - /// - /// - /// Parses a single or . If the text contains - /// more data, then parsing will fail. - /// - /// The parsed . - /// The text. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public static InternetAddress Parse (string text) - { - return Parse (ParserOptions.Default, text); - } - } -} diff --git a/src/MimeKit/InternetAddressList.cs b/src/MimeKit/InternetAddressList.cs deleted file mode 100644 index e989bfc..0000000 --- a/src/MimeKit/InternetAddressList.cs +++ /dev/null @@ -1,1110 +0,0 @@ -// -// InternetAddressList.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections; -using System.Collections.Generic; - -#if ENABLE_SNM -using System.Net.Mail; -#endif - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A list of email addresses. - /// - /// - /// An may contain any number of addresses of any type - /// defined by the original Internet Message specification. - /// There are effectively two (2) types of addresses: mailboxes and groups. - /// Mailbox addresses are what are most commonly known as email addresses and are - /// represented by the class. - /// Group addresses are themselves lists of addresses and are represented by the - /// class. While rare, it is still important to handle these - /// types of addresses. They typically only contain mailbox addresses, but may also - /// contain other group addresses. - /// - public class InternetAddressList : IList, IEquatable, IComparable - { - readonly List list = new List (); - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new containing the supplied addresses. - /// - /// An initial list of addresses. - /// - /// is null. - /// - public InternetAddressList (IEnumerable addresses) - { - if (addresses == null) - throw new ArgumentNullException (nameof (addresses)); - - foreach (var address in addresses) { - address.Changed += AddressChanged; - list.Add (address); - } - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new, empty, . - /// - public InternetAddressList () - { - } - - /// - /// Recursively get all of the mailboxes contained within the . - /// - /// - /// This API is useful for collecting a flattened list of - /// recipients for use with sending via SMTP or for encrypting via S/MIME or PGP/MIME. - /// - /// The mailboxes. - public IEnumerable Mailboxes { - get { - foreach (var address in list) { - var group = address as GroupAddress; - - if (group != null) { - foreach (var mailbox in group.Members.Mailboxes) - yield return mailbox; - } else { - yield return (MailboxAddress) address; - } - } - - yield break; - } - } - - #region IList implementation - - /// - /// Get the index of the specified address. - /// - /// - /// Finds the index of the specified address, if it exists. - /// - /// The index of the specified address if found; otherwise -1. - /// The address to get the index of. - /// - /// is null. - /// - public int IndexOf (InternetAddress address) - { - if (address == null) - throw new ArgumentNullException (nameof (address)); - - return list.IndexOf (address); - } - - /// - /// Insert an address at the specified index. - /// - /// - /// Inserts the address at the specified index in the list. - /// - /// The index to insert the address. - /// The address. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void Insert (int index, InternetAddress address) - { - if (index < 0 || index > list.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (address == null) - throw new ArgumentNullException (nameof (address)); - - address.Changed += AddressChanged; - list.Insert (index, address); - OnChanged (); - } - - /// - /// Remove the address at the specified index. - /// - /// - /// Removes the address at the specified index. - /// - /// The index of the address to remove. - /// - /// is out of range. - /// - public void RemoveAt (int index) - { - if (index < 0 || index >= list.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - list[index].Changed -= AddressChanged; - list.RemoveAt (index); - OnChanged (); - } - - /// - /// Get or set the at the specified index. - /// - /// - /// Gets or sets the at the specified index. - /// - /// The internet address at the specified index. - /// The index of the address to get or set. - /// - /// is null. - /// - /// - /// is out of range. - /// - public InternetAddress this [int index] { - get { return list[index]; } - set { - if (index < 0 || index >= list.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (list[index] == value) - return; - - list[index].Changed -= AddressChanged; - value.Changed += AddressChanged; - list[index] = value; - OnChanged (); - } - } - - #endregion - - #region ICollection implementation - - /// - /// Get the number of addresses in the . - /// - /// - /// Indicates the number of addresses in the list. - /// - /// The number of addresses. - public int Count { - get { return list.Count; } - } - - /// - /// Get a value indicating whether the is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Add an address to the . - /// - /// - /// Adds the specified address to the end of the address list. - /// - /// The address. - /// - /// is null. - /// - public void Add (InternetAddress address) - { - if (address == null) - throw new ArgumentNullException (nameof (address)); - - address.Changed += AddressChanged; - list.Add (address); - OnChanged (); - } - - /// - /// Add a collection of addresses to the . - /// - /// - /// Adds a range of addresses to the end of the address list. - /// - /// A colelction of addresses. - /// - /// is null. - /// - public void AddRange (IEnumerable addresses) - { - if (addresses == null) - throw new ArgumentNullException (nameof (addresses)); - - bool changed = false; - - foreach (var address in addresses) { - address.Changed += AddressChanged; - list.Add (address); - changed = true; - } - - if (changed) - OnChanged (); - } - - /// - /// Clear the address list. - /// - /// - /// Removes all of the addresses from the list. - /// - public void Clear () - { - if (list.Count == 0) - return; - - for (int i = 0; i < list.Count; i++) - list[i].Changed -= AddressChanged; - - list.Clear (); - OnChanged (); - } - - /// - /// Check if the contains the specified address. - /// - /// - /// Determines whether or not the address list contains the specified address. - /// - /// true if the specified address exists; - /// otherwise false. - /// The address. - /// - /// is null. - /// - public bool Contains (InternetAddress address) - { - if (address == null) - throw new ArgumentNullException (nameof (address)); - - return list.Contains (address); - } - - /// - /// Copy all of the addresses in the to the specified array. - /// - /// - /// Copies all of the addresses within the into the array, - /// starting at the specified array index. - /// - /// The array to copy the addresses to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (InternetAddress[] array, int arrayIndex) - { - list.CopyTo (array, arrayIndex); - } - - /// - /// Remove the specified address from the . - /// - /// - /// Removes the specified address. - /// - /// true if the address was removed; otherwise false. - /// The address. - /// - /// is null. - /// - public bool Remove (InternetAddress address) - { - if (address == null) - throw new ArgumentNullException (nameof (address)); - - if (list.Remove (address)) { - address.Changed -= AddressChanged; - OnChanged (); - return true; - } - - return false; - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the list of addresses. - /// - /// - /// Gets an enumerator for the list of addresses. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return list.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the list of addresses. - /// - /// - /// Gets an enumerator for the list of addresses. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return list.GetEnumerator (); - } - - #endregion - - #region IEquatable implementation - - /// - /// Determine whether the specified is equal to the current . - /// - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public bool Equals (InternetAddressList other) - { - if (other == null) - return false; - - if (other.Count != Count) - return false; - - for (int i = 0; i < Count; i++) { - if (!this[i].Equals (other[i])) - return false; - } - - return true; - } - - #endregion - - #region IComparable implementation - - /// - /// Compare two internet address lists. - /// - /// - /// Compares two internet address lists for the purpose of sorting. - /// - /// The sort order of the current internet address list compared to the other internet address list. - /// The internet address list to compare to. - /// - /// is null. - /// - public int CompareTo (InternetAddressList other) - { - int rv; - - if (other == null) - throw new ArgumentNullException (nameof (other)); - - for (int i = 0; i < Math.Min (Count, other.Count); i++) { - if ((rv = this[i].CompareTo (other[i])) != 0) - return rv; - } - - return Count - other.Count; - } - - #endregion - - /// - /// Determine whether the specified object is equal to the current object. - /// - /// - /// The type of comparison between the current instance and the parameter depends on whether - /// the current instance is a reference type or a value type. - /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals (object obj) - { - return Equals (obj as InternetAddressList); - } - - /// - /// Return the hash code for this instance. - /// - /// - /// Returns the hash code for this instance. - /// - /// A hash code for the current object. - public override int GetHashCode () - { - return ToString ().GetHashCode (); - } - - internal void Encode (FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength) - { - for (int i = 0; i < list.Count; i++) { - if (i > 0) { - builder.Append (", "); - lineLength += 2; - } - - list[i].Encode (options, builder, firstToken && i == 0, ref lineLength); - } - } - - /// - /// Serialize an to a string, optionally encoding the list of addresses for transport. - /// - /// - /// If is true, each address in the list will be encoded - /// according to the rules defined in rfc2047. - /// If there are multiple addresses in the list, they will be separated by a comma. - /// - /// A string representing the . - /// The formatting options. - /// If set to true, each in the list will be encoded. - public string ToString (FormatOptions options, bool encode) - { - var builder = new StringBuilder (); - - if (encode) { - int lineLength = 0; - - Encode (options, builder, true, ref lineLength); - - return builder.ToString (); - } - - for (int i = 0; i < list.Count; i++) { - if (i > 0) - builder.Append (", "); - - builder.Append (list[i].ToString (options, false)); - } - - return builder.ToString (); - } - - /// - /// Serialize an to a string, optionally encoding the list of addresses for transport. - /// - /// - /// If is true, each address in the list will be encoded - /// according to the rules defined in rfc2047. - /// If there are multiple addresses in the list, they will be separated by a comma. - /// - /// A string representing the . - /// If set to true, each in the list will be encoded. - public string ToString (bool encode) - { - return ToString (FormatOptions.Default, encode); - } - - /// - /// Serialize an to a string suitable for display. - /// - /// - /// If there are multiple addresses in the list, they will be separated by a comma. - /// - /// A string representing the . - public override string ToString () - { - return ToString (FormatOptions.Default, false); - } - - internal event EventHandler Changed; - - void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - - void AddressChanged (object sender, EventArgs e) - { - OnChanged (); - } - - internal static bool TryParse (ParserOptions options, byte[] text, ref int index, int endIndex, bool isGroup, int groupDepth, bool throwOnError, out List addresses) - { - var flags = throwOnError ? InternetAddress.AddressParserFlags.Parse : InternetAddress.AddressParserFlags.TryParse; - var list = new List (); - InternetAddress address; - - addresses = null; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index == endIndex) { - if (throwOnError) - throw new ParseException ("No addresses found.", index, index); - - return false; - } - - while (index < endIndex) { - if (isGroup && text[index] == (byte) ';') - break; - - if (!InternetAddress.TryParse (options, text, ref index, endIndex, groupDepth, flags, out address)) { - // skip this address... - while (index < endIndex && text[index] != (byte) ',' && (!isGroup || text[index] != (byte) ';')) - index++; - } else { - list.Add (address); - } - - // Note: we loop here in case there are any null addresses between commas - do { - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex || text[index] != (byte) ',') - break; - - index++; - } while (true); - } - - addresses = list; - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true, if the address list was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed addresses. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out InternetAddressList addresses) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - List addrlist; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, startIndex + length, false, 0, false, out addrlist)) { - addresses = null; - return false; - } - - addresses = new InternetAddressList (addrlist); - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// true, if the address list was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed addresses. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out InternetAddressList addresses) - { - return TryParse (ParserOptions.Default, buffer, startIndex, length, out addresses); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the supplied buffer starting at the specified index. - /// - /// true, if the address list was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed addresses. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, out InternetAddressList addresses) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - List addrlist; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, buffer.Length, false, 0, false, out addrlist)) { - addresses = null; - return false; - } - - addresses = new InternetAddressList (addrlist); - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the supplied buffer starting at the specified index. - /// - /// true, if the address list was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed addresses. - /// - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (byte[] buffer, int startIndex, out InternetAddressList addresses) - { - return TryParse (ParserOptions.Default, buffer, startIndex, out addresses); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the specified buffer. - /// - /// true, if the address list was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The parsed addresses. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, out InternetAddressList addresses) - { - ParseUtils.ValidateArguments (options, buffer); - - List addrlist; - int index = 0; - - if (!TryParse (options, buffer, ref index, buffer.Length, false, 0, false, out addrlist)) { - addresses = null; - return false; - } - - addresses = new InternetAddressList (addrlist); - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the specified buffer. - /// - /// true, if the address list was successfully parsed, false otherwise. - /// The input buffer. - /// The parsed addresses. - /// - /// is null. - /// - public static bool TryParse (byte[] buffer, out InternetAddressList addresses) - { - return TryParse (ParserOptions.Default, buffer, out addresses); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a list of addresses from the specified text. - /// - /// true, if the address list was successfully parsed, false otherwise. - /// The parser options to use. - /// The text. - /// The parsed addresses. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, string text, out InternetAddressList addresses) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - List addrlist; - int index = 0; - - if (!TryParse (options, buffer, ref index, buffer.Length, false, 0, false, out addrlist)) { - addresses = null; - return false; - } - - addresses = new InternetAddressList (addrlist); - - return true; - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a list of addresses from the specified text. - /// - /// true, if the address list was successfully parsed, false otherwise. - /// The text. - /// The parsed addresses. - /// - /// is null. - /// - public static bool TryParse (string text, out InternetAddressList addresses) - { - return TryParse (ParserOptions.Default, text, out addresses); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// could not be parsed. - /// - public static InternetAddressList Parse (ParserOptions options, byte[] buffer, int startIndex, int length) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - List addrlist; - int index = startIndex; - - TryParse (options, buffer, ref index, startIndex + length, false, 0, true, out addrlist); - - return new InternetAddressList (addrlist); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the supplied buffer starting at the given index - /// and spanning across the specified number of bytes. - /// - /// The parsed . - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// could not be parsed. - /// - public static InternetAddressList Parse (byte[] buffer, int startIndex, int length) - { - return Parse (ParserOptions.Default, buffer, startIndex, length); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the supplied buffer starting at the specified index. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// could not be parsed. - /// - public static InternetAddressList Parse (ParserOptions options, byte[] buffer, int startIndex) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - List addrlist; - int index = startIndex; - - TryParse (options, buffer, ref index, buffer.Length, false, 0, true, out addrlist); - - return new InternetAddressList (addrlist); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the supplied buffer starting at the specified index. - /// - /// The parsed . - /// The input buffer. - /// The starting index of the input buffer. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// could not be parsed. - /// - public static InternetAddressList Parse (byte[] buffer, int startIndex) - { - return Parse (ParserOptions.Default, buffer, startIndex); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the specified buffer. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// could not be parsed. - /// - public static InternetAddressList Parse (ParserOptions options, byte[] buffer) - { - ParseUtils.ValidateArguments (options, buffer); - - List addrlist; - int index = 0; - - TryParse (options, buffer, ref index, buffer.Length, false, 0, true, out addrlist); - - return new InternetAddressList (addrlist); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a list of addresses from the specified buffer. - /// - /// The parsed . - /// The input buffer. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public static InternetAddressList Parse (byte[] buffer) - { - return Parse (ParserOptions.Default, buffer); - } - - /// - /// Parse the given text into a new instance. - /// - /// - /// Parses a list of addresses from the specified text. - /// - /// The parsed . - /// The parser options to use. - /// The text. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// could not be parsed. - /// - public static InternetAddressList Parse (ParserOptions options, string text) - { - ParseUtils.ValidateArguments (options, text); - - var buffer = Encoding.UTF8.GetBytes (text); - List addrlist; - int index = 0; - - TryParse (options, buffer, ref index, buffer.Length, false, 0, true, out addrlist); - - return new InternetAddressList (addrlist); - } - - /// - /// Parse the given text into a new instance. - /// - /// - /// Parses a list of addresses from the specified text. - /// - /// The parsed . - /// The text. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public static InternetAddressList Parse (string text) - { - return Parse (ParserOptions.Default, text); - } - -#if ENABLE_SNM - /// - /// Explicit cast to convert a to a - /// . - /// - /// - /// Casts a to a - /// in cases where you might want to make use of the System.Net.Mail APIs. - /// - /// The equivalent . - /// The addresses. - /// - /// contains one or more group addresses and cannot be converted. - /// - public static explicit operator MailAddressCollection (InternetAddressList addresses) - { - if (addresses == null) - return null; - - var collection = new MailAddressCollection (); - for (int i = 0; i < addresses.Count; i++) { - if (addresses[i] is GroupAddress) - throw new InvalidCastException ("Cannot cast a MailKit.GroupAddress to a System.Net.Mail.MailAddress."); - - var mailbox = (MailboxAddress) addresses[i]; - - collection.Add ((MailAddress) mailbox); - } - - return collection; - } - - /// - /// Explicit cast to convert a - /// to a . - /// - /// - /// Casts a to a - /// in cases where you might want to make use of the the superior MimeKit APIs. - /// - /// The equivalent . - /// The mail address. - public static explicit operator InternetAddressList (MailAddressCollection addresses) - { - if (addresses == null) - return null; - - var list = new InternetAddressList (); - foreach (var address in addresses) - list.Add ((MailboxAddress) address); - - return list; - } -#endif - } -} diff --git a/src/MimeKit/MacInterop/CFArray.cs b/src/MimeKit/MacInterop/CFArray.cs deleted file mode 100644 index 9e38c75..0000000 --- a/src/MimeKit/MacInterop/CFArray.cs +++ /dev/null @@ -1,56 +0,0 @@ -// -// CFArray.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - class CFArray : CFObject - { - [DllImport (CoreFoundationLibrary)] - extern static IntPtr CFArrayGetValueAtIndex (IntPtr handle, IntPtr index); - - [DllImport (CoreFoundationLibrary)] - extern static int CFArrayGetCount (IntPtr handle); - - public CFArray (IntPtr handle, bool owns) : base (handle, owns) - { - } - - public CFArray (IntPtr handle) : base (handle, false) - { - } - - public int Count { - get { return CFArrayGetCount (Handle); } - } - - public IntPtr GetValue (int index) - { - return CFArrayGetValueAtIndex (Handle, new IntPtr (index)); - } - } -} diff --git a/src/MimeKit/MacInterop/CFData.cs b/src/MimeKit/MacInterop/CFData.cs deleted file mode 100644 index eda7499..0000000 --- a/src/MimeKit/MacInterop/CFData.cs +++ /dev/null @@ -1,85 +0,0 @@ -// -// CFData.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - class CFData : CFObject - { - byte[] cached; - - [DllImport (CoreFoundationLibrary)] - extern static int CFDataGetLength (IntPtr handle); - - [DllImport (CoreFoundationLibrary)] - extern static void CFDataGetBytes (IntPtr handle, CFRange range, IntPtr buffer); - - [DllImport (CoreFoundationLibrary)] - extern static IntPtr CFDataCreate (IntPtr allocator, byte[] buffer, int length); - - static byte[] CFDataGetBytes (IntPtr handle) - { - if (handle == IntPtr.Zero) - return null; - - int length = CFDataGetLength (handle); - if (length < 1) - return null; - - var buffer = new byte[length]; - unsafe { - fixed (byte *bufptr = buffer) { - CFDataGetBytes (handle, new CFRange (0, length), (IntPtr) bufptr); - } - } - - return buffer; - } - - public CFData (IntPtr handle, bool owns) : base (handle, owns) - { - } - - public CFData (IntPtr handle) : base (handle, false) - { - } - - public CFData (byte[] buffer) - { - Handle = CFDataCreate (IntPtr.Zero, buffer, buffer.Length); - cached = buffer; - } - - public byte[] GetBuffer () - { - if (cached == null) - cached = CFDataGetBytes (Handle); - - return cached; - } - } -} diff --git a/src/MimeKit/MacInterop/CFDictionary.cs b/src/MimeKit/MacInterop/CFDictionary.cs deleted file mode 100644 index 937a355..0000000 --- a/src/MimeKit/MacInterop/CFDictionary.cs +++ /dev/null @@ -1,177 +0,0 @@ -// -// CFDictionary.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - class CFDictionary : CFObject - { - public static IntPtr KeyCallbacks; - public static IntPtr ValueCallbacks; - - static CFDictionary () - { - var lib = Dlfcn.dlopen (CoreFoundationLibrary, 0); - - try { - KeyCallbacks = Dlfcn.GetIndirect (lib, "kCFTypeDictionaryKeyCallBacks"); - ValueCallbacks = Dlfcn.GetIndirect (lib, "kCFTypeDictionaryValueCallBacks"); - } finally { - Dlfcn.dlclose (lib); - } - } - - public CFDictionary (IntPtr handle, bool owns) : base (handle, owns) - { - } - - public CFDictionary (IntPtr handle) : base (handle, false) - { - } - - public static CFDictionary FromObjectAndKey (CFObject obj, CFObject key) - { - return new CFDictionary (CFDictionaryCreate (IntPtr.Zero, new IntPtr[] { key.Handle }, new IntPtr [] { obj.Handle }, 1, KeyCallbacks, ValueCallbacks), true); - } - - public static CFDictionary FromObjectsAndKeys (CFObject[] objects, CFObject[] keys) - { - if (objects == null) - throw new ArgumentNullException ("objects"); - - if (keys == null) - throw new ArgumentNullException ("keys"); - - if (objects.Length != keys.Length) - throw new ArgumentException ("The length of both arrays must be the same"); - - IntPtr [] k = new IntPtr [keys.Length]; - IntPtr [] v = new IntPtr [keys.Length]; - - for (int i = 0; i < k.Length; i++) { - k [i] = keys [i].Handle; - v [i] = objects [i].Handle; - } - - return new CFDictionary (CFDictionaryCreate (IntPtr.Zero, k, v, k.Length, KeyCallbacks, ValueCallbacks), true); - } - - [DllImport (CoreFoundationLibrary)] - extern static IntPtr CFDictionaryCreate (IntPtr allocator, IntPtr[] keys, IntPtr[] vals, int len, IntPtr keyCallbacks, IntPtr valCallbacks); - - [DllImport (CoreFoundationLibrary)] - extern static IntPtr CFDictionaryGetValue (IntPtr theDict, IntPtr key); - public static IntPtr GetValue (IntPtr theDict, IntPtr key) - { - return CFDictionaryGetValue (theDict, key); - } - -// public static bool GetBooleanValue (IntPtr theDict, IntPtr key) -// { -// var value = GetValue (theDict, key); -// if (value == IntPtr.Zero) -// return false; -// return CFBoolean.GetValue (value); -// } - - public string GetStringValue (string key) - { - using (var str = new CFString (key)) { - using (var value = new CFString (CFDictionaryGetValue (Handle, str.Handle))) { - return value.ToString (); - } - } - } - - public int GetInt32Value (string key) - { - int value = 0; - using (var str = new CFString (key)) { - if (!CFNumberGetValue (CFDictionaryGetValue (Handle, str.Handle), /* kCFNumberSInt32Type */ 3, out value)) - throw new System.Collections.Generic.KeyNotFoundException (string.Format ("Key {0} not found", key)); - return value; - } - } - - public IntPtr GetIntPtrValue (string key) - { - using (var str = new CFString (key)) { - return CFDictionaryGetValue (Handle, str.Handle); - } - } - - public CFDictionary GetDictionaryValue (string key) - { - using (var str = new CFString (key)) { - var ptr = CFDictionaryGetValue (Handle, str.Handle); - return ptr == IntPtr.Zero ? null : new CFDictionary (ptr); - } - } - - public bool ContainsKey (string key) - { - using (var str = new CFString (key)) { - return CFDictionaryContainsKey (Handle, str.Handle); - } - } - - [DllImport (CoreFoundationLibrary)] - static extern bool CFNumberGetValue (IntPtr number, int theType, out int value); - - [DllImport (CoreFoundationLibrary)] - extern static bool CFDictionaryContainsKey (IntPtr theDict, IntPtr key); - } - - class CFMutableDictionary : CFDictionary - { - [DllImport (CoreFoundationLibrary)] - static extern IntPtr CFDictionaryCreateMutable (IntPtr allocator, IntPtr capacity, IntPtr keyCallBacks, IntPtr valueCallBacks); - - // void CFDictionaryAddValue (CFMutableDictionaryRef theDict, const void *key, const void *value); - - [DllImport (CoreFoundationLibrary)] - extern static void CFDictionarySetValue (IntPtr theDict, IntPtr key, IntPtr value); - - public CFMutableDictionary (IntPtr handle, bool owns) : base (handle, owns) - { - } - - public CFMutableDictionary (IntPtr handle) : base (handle, false) - { - } - - public void SetValue (IntPtr key, IntPtr value) - { - CFDictionarySetValue (Handle, key, value); - } - -// public void SetValue (IntPtr key, bool value) -// { -// SetValue (key, value ? CFBoolean.True.Handle : CFBoolean.False.Handle); -// } - } -} diff --git a/src/MimeKit/MacInterop/CFObject.cs b/src/MimeKit/MacInterop/CFObject.cs deleted file mode 100644 index 00d4173..0000000 --- a/src/MimeKit/MacInterop/CFObject.cs +++ /dev/null @@ -1,76 +0,0 @@ -// -// CFObject.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - abstract class CFObject : IDisposable - { - protected const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; - - [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] - internal extern static IntPtr CFRelease (IntPtr handle); - - [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] - internal extern static IntPtr CFRetain (IntPtr handle); - - public IntPtr Handle { - get; protected set; - } - - protected CFObject (IntPtr handle, bool owns) - { - if (!owns) - CFRetain (handle); - - Handle = handle; - } - - protected CFObject () - { - } - - ~CFObject () - { - Dispose (false); - } - - protected virtual void Dispose (bool disposing) - { - if (Handle != IntPtr.Zero){ - CFRelease (Handle); - Handle = IntPtr.Zero; - } - } - - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - } - } -} diff --git a/src/MimeKit/MacInterop/CFRange.cs b/src/MimeKit/MacInterop/CFRange.cs deleted file mode 100644 index 8519c15..0000000 --- a/src/MimeKit/MacInterop/CFRange.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// CFRange.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - [StructLayout (LayoutKind.Sequential)] - struct CFRange { - readonly IntPtr location; - readonly IntPtr length; - - public int Location { - get { return location.ToInt32 (); } - } - - public int Length { - get { return length.ToInt32 (); } - } - - public CFRange (int location, int length) - : this ((long) location, (long) length) - { - } - - public CFRange (long location, long length) - { - this.location = new IntPtr (location); - this.length = new IntPtr (length); - } - } -} diff --git a/src/MimeKit/MacInterop/CFString.cs b/src/MimeKit/MacInterop/CFString.cs deleted file mode 100644 index 6c856fe..0000000 --- a/src/MimeKit/MacInterop/CFString.cs +++ /dev/null @@ -1,131 +0,0 @@ -// -// CFString.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - class CFString : CFObject - { - string cached; - - [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] - extern static IntPtr CFStringCreateWithCharacters (IntPtr allocator, string str, int count); - - [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] - extern static int CFStringGetLength (IntPtr handle); - - [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] - extern static IntPtr CFStringGetCharactersPtr (IntPtr handle); - - [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] - extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer); - - public CFString (string str) - { - if (str == null) - throw new ArgumentNullException ("str"); - - Handle = CFStringCreateWithCharacters (IntPtr.Zero, str, str.Length); - this.cached = str; - } - - public CFString (IntPtr handle, bool owns) : base (handle, owns) - { - } - - public CFString (IntPtr handle) : base (handle, false) - { - } - - static string CFStringGetString (IntPtr handle) - { - if (handle == IntPtr.Zero) - return null; - - string str; - - int length = CFStringGetLength (handle); - IntPtr unicode = CFStringGetCharactersPtr (handle); - IntPtr buffer = IntPtr.Zero; - - if (unicode == IntPtr.Zero) { - CFRange range = new CFRange (0, length); - buffer = Marshal.AllocCoTaskMem (length * 2); - CFStringGetCharacters (handle, range, buffer); - unicode = buffer; - } - - unsafe { - str = new string ((char *) unicode, 0, length); - } - - if (buffer != IntPtr.Zero) - Marshal.FreeCoTaskMem (buffer); - - return str; - } - - public static implicit operator string (CFString str) - { - return str.ToString (); - } - - public static implicit operator CFString (string str) - { - return new CFString (str); - } - - public int Length { - get { - if (cached != null) - return cached.Length; - - return CFStringGetLength (Handle); - } - } - - [DllImport (CoreFoundationLibrary, CharSet=CharSet.Unicode)] - extern static char CFStringGetCharacterAtIndex (IntPtr handle, int p); - - public char this [int index] { - get { - if (cached != null) - return cached[index]; - - return CFStringGetCharacterAtIndex (Handle, index); - } - } - - public override string ToString () - { - if (cached == null) - cached = CFStringGetString (Handle); - - return cached; - } - } -} diff --git a/src/MimeKit/MacInterop/CssmDbAttributeFormat.cs b/src/MimeKit/MacInterop/CssmDbAttributeFormat.cs deleted file mode 100644 index 21d71e9..0000000 --- a/src/MimeKit/MacInterop/CssmDbAttributeFormat.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// CssmDbAttributeFormat.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - enum CssmDbAttributeFormat : int { - String = 0, - Int32 = 1, - UInt32 = 2, - BigNum = 3, - Real = 4, - DateTime = 5, - Blob = 6, - MultiUInt32 = 7, - Complex = 8 - } -} diff --git a/src/MimeKit/MacInterop/CssmKeyUse.cs b/src/MimeKit/MacInterop/CssmKeyUse.cs deleted file mode 100644 index ced4315..0000000 --- a/src/MimeKit/MacInterop/CssmKeyUse.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// CssmKeyUse.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - [Flags] - enum CssmKeyUse : uint { - Any = 0x80000000, - Encrypt = 0x00000001, - Decrypt = 0x00000002, - Sign = 0x00000004, - Verify = 0x00000008, - SignRecover = 0x00000010, - VerifyRecover = 0x00000020, - Wrap = 0x00000040, - Unwrap = 0x00000080, - Derive = 0x00000100 - } -} diff --git a/src/MimeKit/MacInterop/CssmTPAppleCertStatus.cs b/src/MimeKit/MacInterop/CssmTPAppleCertStatus.cs deleted file mode 100644 index 3b09f36..0000000 --- a/src/MimeKit/MacInterop/CssmTPAppleCertStatus.cs +++ /dev/null @@ -1,39 +0,0 @@ -// -// CssmTPAppleCertStatus.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - [Flags] - enum CssmTPAppleCertStatus : uint { - Expired = 0x00000001, - NotValidYet = 0x00000002, - IsInInputCerts = 0x00000004, - IsInAnchors = 0x00000008, - IsRoot = 0x00000010, - IsFromNet = 0x00000020 - } -} diff --git a/src/MimeKit/MacInterop/Dlfcn.cs b/src/MimeKit/MacInterop/Dlfcn.cs deleted file mode 100644 index 9d9e4d7..0000000 --- a/src/MimeKit/MacInterop/Dlfcn.cs +++ /dev/null @@ -1,228 +0,0 @@ -// -// Dlfcn.cs: Support for looking up symbols in shared libraries -// -// Authors: -// Jonathan Pryor: -// Miguel de Icaza. -// -// Copyright 2009-2010, Novell, Inc. -// Copyright 2011, 2012 Xamarin Inc -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - static class Dlfcn - { - const string SystemLibrary = "/usr/lib/libSystem.dylib"; - - [DllImport (SystemLibrary)] - public static extern int dlclose (IntPtr handle); - - [DllImport (SystemLibrary)] - public static extern IntPtr dlopen (string path, int mode); - - [DllImport (SystemLibrary)] - public static extern IntPtr dlsym (IntPtr handle, string symbol); - - [DllImport (SystemLibrary, EntryPoint = "dlerror")] - internal static extern IntPtr _dlerror (); - - public static string dlerror () - { - // we can't free the string returned from dlerror - return Marshal.PtrToStringAnsi (_dlerror ()); - } - - public static CFString GetStringConstant (IntPtr handle, string symbol) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return null; - - var actual = Marshal.ReadIntPtr (indirect); - if (actual == IntPtr.Zero) - return null; - - return new CFString (actual); - } - - public static IntPtr GetIndirect (IntPtr handle, string symbol) - { - return dlsym (handle, symbol); - } - - public static int GetInt32 (IntPtr handle, string symbol) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return 0; - return Marshal.ReadInt32 (indirect); - } - - public static void SetInt32 (IntPtr handle, string symbol, int value) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return; - Marshal.WriteInt32 (indirect, value); - } - - public static long GetInt64 (IntPtr handle, string symbol) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return 0; - return Marshal.ReadInt64 (indirect); - } - - public static void SetInt64 (IntPtr handle, string symbol, long value) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return; - Marshal.WriteInt64 (indirect, value); - } - - public static IntPtr GetIntPtr (IntPtr handle, string symbol) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return IntPtr.Zero; - return Marshal.ReadIntPtr (indirect); - } - - public static void SetIntPtr (IntPtr handle, string symbol, IntPtr value) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return; - Marshal.WriteIntPtr (indirect, value); - } - - public static double GetDouble (IntPtr handle, string symbol) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return 0; - unsafe { - double *d = (double *) indirect; - - return *d; - } - } - - public static void SetDouble (IntPtr handle, string symbol, double value) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return; - unsafe { - *(double *) indirect = value; - } - } - - public static float GetFloat (IntPtr handle, string symbol) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return 0; - unsafe { - float *d = (float *) indirect; - - return *d; - } - } - - public static void SetFloat (IntPtr handle, string symbol, float value) - { - var indirect = dlsym (handle, symbol); - if (indirect == IntPtr.Zero) - return; - unsafe { - *(float *) indirect = value; - } - } - - internal static int SlowGetInt32 (string lib, string symbol) - { - var handle = dlopen (lib, 0); - if (handle == IntPtr.Zero) - return 0; - try { - return GetInt32 (handle, symbol); - } finally { - dlclose (handle); - } - } - - internal static long SlowGetInt64 (string lib, string symbol) - { - var handle = dlopen (lib, 0); - if (handle == IntPtr.Zero) - return 0; - try { - return GetInt64 (handle, symbol); - } finally { - dlclose (handle); - } - } - - internal static IntPtr SlowGetIntPtr (string lib, string symbol) - { - var handle = dlopen (lib, 0); - if (handle == IntPtr.Zero) - return IntPtr.Zero; - try { - return GetIntPtr (handle, symbol); - } finally { - dlclose (handle); - } - } - - internal static double SlowGetDouble (string lib, string symbol) - { - var handle = dlopen (lib, 0); - if (handle == IntPtr.Zero) - return 0; - try { - return GetDouble (handle, symbol); - } finally { - dlclose (handle); - } - } - - internal static CFString SlowGetStringConstant (string lib, string symbol) - { - var handle = dlopen (lib, 0); - if (handle == IntPtr.Zero) - return null; - - try { - return GetStringConstant (handle, symbol); - } finally { - dlclose (handle); - } - } - } -} diff --git a/src/MimeKit/MacInterop/OSStatus.cs b/src/MimeKit/MacInterop/OSStatus.cs deleted file mode 100644 index d3369f0..0000000 --- a/src/MimeKit/MacInterop/OSStatus.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// OSStatus.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - enum OSStatus { - Ok = 0, - AuthFailed = -25293, - NoSuchKeychain = -25294, - DuplicateKeychain = -25296, - DuplicateItem = -25299, - ItemNotFound = -25300, - NoDefaultKeychain = -25307, - DecodeError = -26275, - } -} diff --git a/src/MimeKit/MacInterop/SecCertificate.cs b/src/MimeKit/MacInterop/SecCertificate.cs deleted file mode 100644 index 2cc3cc4..0000000 --- a/src/MimeKit/MacInterop/SecCertificate.cs +++ /dev/null @@ -1,69 +0,0 @@ -// -// SecCertificate.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - class SecCertificate : CFObject - { - const string SecurityLibrary = "/System/Library/Frameworks/Security.framework/Security"; - - [DllImport (SecurityLibrary)] - static extern IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr data); - - [DllImport (SecurityLibrary)] - static extern IntPtr SecCertificateCopyData (IntPtr certificate); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecCertificateCopyCommonName (IntPtr certificate, out IntPtr commonName); - - public SecCertificate (IntPtr handle, bool own) : base (handle, own) - { - } - - public SecCertificate (IntPtr handle) : base (handle, false) - { - } - - public static SecCertificate Create (CFData data) - { - return new SecCertificate (SecCertificateCreateWithData (IntPtr.Zero, data.Handle), true); - } - - public static SecCertificate Create (byte[] rawData) - { - using (var data = new CFData (rawData)) { - return Create (data); - } - } - - public CFData GetData () - { - return new CFData (SecCertificateCopyData (Handle), true); - } - } -} diff --git a/src/MimeKit/MacInterop/SecExternalFormat.cs b/src/MimeKit/MacInterop/SecExternalFormat.cs deleted file mode 100644 index 9857ef6..0000000 --- a/src/MimeKit/MacInterop/SecExternalFormat.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// SecExternalFormat.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - enum SecExternalFormat : uint { - Unknown = 0, - - // Asymmetric Key Formats - OpenSSL, - SSH, - BSAFE, - SSHv2, - - // Symmetric Key Formats - RawKey, - - // Formats for wrapped symmetric and private keys - WrappedPKCS8, - WrappedOpenSSL, - WrappedSSH, - WrappedLSH, // not supported - - // Formats for certificates - X509Cert, - - // Aggregate Types - PEMSequence, - PKCS7, - PKCS12, - NetscapeCertSequence - } -} diff --git a/src/MimeKit/MacInterop/SecItemAttr.cs b/src/MimeKit/MacInterop/SecItemAttr.cs deleted file mode 100644 index e1f1538..0000000 --- a/src/MimeKit/MacInterop/SecItemAttr.cs +++ /dev/null @@ -1,60 +0,0 @@ -// -// SecItemAttr.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - enum SecItemAttr : int { - CreationDate = 1667522932, - ModDate = 1835295092, - Description = 1684370275, - Comment = 1768123764, - Creator = 1668445298, - Type = 1954115685, - ScriptCode = 1935897200, - Label = 1818321516, - Invisible = 1768846953, - Negative = 1852139361, - CustomIcon = 1668641641, - Account = 1633903476, - Service = 1937138533, - Generic = 1734700641, - SecurityDomain = 1935961454, - Server = 1936881266, - AuthType = 1635023216, - Port = 1886351988, - Path = 1885434984, - Volume = 1986817381, - Address = 1633969266, - Signature = 1936943463, - Protocol = 1886675820, - CertificateType = 1668577648, - CertificateEncoding = 1667591779, - CrlType = 1668445296, - CrlEncoding = 1668443747, - Alias = 1634494835, - } -} diff --git a/src/MimeKit/MacInterop/SecItemClass.cs b/src/MimeKit/MacInterop/SecItemClass.cs deleted file mode 100644 index cf6ddc9..0000000 --- a/src/MimeKit/MacInterop/SecItemClass.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// SecItemClass.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - enum SecItemClass : uint - { - InternetPassword = 1768842612, // 'inet' - GenericPassword = 1734700656, // 'genp' - AppleSharePassword = 1634953328, // 'ashp' - Certificate = 0x80000000 + 0x1000, - PublicKey = 0x0000000A + 5, - PrivateKey = 0x0000000A + 6, - SymmetricKey = 0x0000000A + 7 - } -} diff --git a/src/MimeKit/MacInterop/SecItemExportFlags.cs b/src/MimeKit/MacInterop/SecItemExportFlags.cs deleted file mode 100644 index feecf84..0000000 --- a/src/MimeKit/MacInterop/SecItemExportFlags.cs +++ /dev/null @@ -1,34 +0,0 @@ -// -// SecItemExportFlags.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - enum SecItemImportExportFlags : uint { - None = 0x00000000, - PemArmour = 0x00000001, - } -} diff --git a/src/MimeKit/MacInterop/SecKeyAttribute.cs b/src/MimeKit/MacInterop/SecKeyAttribute.cs deleted file mode 100644 index 789e854..0000000 --- a/src/MimeKit/MacInterop/SecKeyAttribute.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// SecKeyAttribute.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.MacInterop { - enum SecKeyAttribute { - KeyClass = 0, - PrintName = 1, - Alias = 2, - Permanent = 3, - Private = 4, - Modifiable = 5, - Label = 6, - ApplicationTag = 7, - KeyCreator = 8, - KeyType = 9, - KeySizeInBits = 10, - EffectiveKeySize = 11, - StartDate = 12, - EndDate = 13, - Sensitive = 14, - AlwaysSensitive = 15, - Extractable = 16, - NeverExtractable = 17, - Encrypt = 18, - Decrypt = 19, - Derive = 20, - Sign = 21, - Verify = 22, - SignRecover = 23, - VerifyRecover = 24, - Wrap = 25, - Unwrap = 26, - } -} diff --git a/src/MimeKit/MacInterop/SecKeychain.cs b/src/MimeKit/MacInterop/SecKeychain.cs deleted file mode 100644 index 89ae58f..0000000 --- a/src/MimeKit/MacInterop/SecKeychain.cs +++ /dev/null @@ -1,471 +0,0 @@ -// -// SecKeychain.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Diagnostics; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -using Org.BouncyCastle.Security.Certificates; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.X509; -using Org.BouncyCastle.Pkcs; - -using MimeKit.Cryptography; - -namespace MimeKit.MacInterop { - class SecKeychain : CFObject - { - const string SecurityLibrary = "/System/Library/Frameworks/Security.framework/Security"; - - /// - /// The default login keychain. - /// - public static readonly SecKeychain Default = GetDefault (); - - bool disposed; - - SecKeychain (IntPtr handle, bool owns) : base (handle, owns) - { - } - - SecKeychain (IntPtr handle) : base (handle, false) - { - } - - #region Managing Certificates - - [DllImport (SecurityLibrary)] - static extern OSStatus SecCertificateAddToKeychain (IntPtr certificate, IntPtr keychain); - - [DllImport (SecurityLibrary)] - static extern IntPtr SecCertificateCreateWithData (IntPtr allocator, IntPtr data); - - [DllImport (SecurityLibrary)] - static extern IntPtr SecCertificateCopyData (IntPtr certificate); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecCertificateCopyCommonName (IntPtr certificate, out IntPtr commonName); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecPKCS12Import (IntPtr pkcs12DataRef, IntPtr options, ref IntPtr items); - - #endregion - - #region Managing Identities - - [DllImport (SecurityLibrary)] - static extern OSStatus SecIdentityCopyCertificate (IntPtr identityRef, out IntPtr certificateRef); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecIdentityCopyPrivateKey (IntPtr identityRef, out IntPtr privateKeyRef); - - // WARNING: deprecated in Mac OS X 10.7 - [DllImport (SecurityLibrary)] - static extern OSStatus SecIdentitySearchCreate (IntPtr keychainOrArray, CssmKeyUse keyUsage, out IntPtr searchRef); - - // WARNING: deprecated in Mac OS X 10.7 - [DllImport (SecurityLibrary)] - static extern OSStatus SecIdentitySearchCopyNext (IntPtr searchRef, out IntPtr identity); - - // Note: SecIdentitySearch* has been replaced with SecItemCopyMatching - - //[DllImport (SecurityLib)] - //OSStatus SecItemCopyMatching (CFDictionaryRef query, CFTypeRef *result); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecItemImport (IntPtr importedData, IntPtr fileName, ref SecExternalFormat format, IntPtr type, SecItemImportExportFlags flags, IntPtr keyParams, IntPtr keychain, ref IntPtr items); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecItemExport (IntPtr itemRef, SecExternalFormat format, SecItemImportExportFlags flags, IntPtr keyParams, out IntPtr exportedData); - - #endregion - - #region Getting Information About Security Result Codes - - [DllImport (SecurityLibrary)] - static extern IntPtr SecCopyErrorMessageString (OSStatus status, IntPtr reserved); - - #endregion - - #region Managing Keychains - - [DllImport (SecurityLibrary)] - static extern OSStatus SecKeychainCopyDefault (ref IntPtr keychain); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecKeychainCreate (string path, uint passwordLength, byte[] password, bool promptUser, IntPtr initialAccess, ref IntPtr keychain); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecKeychainOpen (string path, ref IntPtr keychain); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecKeychainDelete (IntPtr keychain); - - static SecKeychain GetDefault () - { - IntPtr handle = IntPtr.Zero; - - if (SecKeychainCopyDefault (ref handle) == OSStatus.Ok) - return new SecKeychain (handle, true); - - return null; - } - - /// - /// Create a keychain at the specified path with the specified password. - /// - /// The path to the keychain. - /// The password for unlocking the keychain. - /// - /// was null. - /// -or- - /// was null. - /// - /// - /// An unknown error creating the keychain occurred. - /// - public static SecKeychain Create (string path, string password) - { - if (path == null) - throw new ArgumentNullException ("path"); - - if (password == null) - throw new ArgumentNullException ("password"); - - var passwd = Encoding.UTF8.GetBytes (password); - var handle = IntPtr.Zero; - - var status = SecKeychainCreate (path, (uint) passwd.Length, passwd, false, IntPtr.Zero, ref handle); - if (status != OSStatus.Ok) - throw new Exception (GetError (status)); - - return new SecKeychain (handle); - } - - /// - /// Opens the keychain at the specified path. - /// - /// The path to the keychain. - /// - /// was null. - /// - /// - /// An unknown error opening the keychain occurred. - /// - public static SecKeychain Open (string path) - { - if (path == null) - throw new ArgumentNullException ("path"); - - var handle = IntPtr.Zero; - - var status = SecKeychainOpen (path, ref handle); - if (status != OSStatus.Ok) - throw new Exception (GetError (status)); - - return new SecKeychain (handle); - } - - /// - /// Deletes the specified keychain. - /// - /// Keychain. - /// - /// was null. - /// - /// - /// has been disposed. - /// - /// - /// An unknown error deleting the keychain occurred. - /// - public static void Delete (SecKeychain keychain) - { - if (keychain == null) - throw new ArgumentNullException ("keychain"); - - if (keychain.disposed) - throw new ObjectDisposedException ("SecKeychain"); - - if (keychain.Handle == IntPtr.Zero) - throw new InvalidOperationException (); - - var status = SecKeychainDelete (keychain.Handle); - if (status != OSStatus.Ok) - throw new Exception (GetError (status)); - - keychain.Dispose (); - } - - #endregion - - #region Searching for Keychain Items - - [DllImport (SecurityLibrary)] - static extern unsafe OSStatus SecKeychainSearchCreateFromAttributes (IntPtr keychainOrArray, SecItemClass itemClass, SecKeychainAttributeList *attrList, out IntPtr searchRef); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecKeychainSearchCopyNext (IntPtr searchRef, out IntPtr itemRef); - - #endregion - - #region Creating and Deleting Keychain Items - - [DllImport (SecurityLibrary)] - static extern unsafe OSStatus SecKeychainItemCreateFromContent (SecItemClass itemClass, SecKeychainAttributeList *attrList, - uint passwordLength, byte[] password, IntPtr keychain, - IntPtr initialAccess, ref IntPtr itemRef); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecKeychainItemDelete (IntPtr itemRef); - - #endregion - - #region Managing Keychain Items - - [DllImport (SecurityLibrary)] - static extern unsafe OSStatus SecKeychainItemModifyAttributesAndData (IntPtr itemRef, SecKeychainAttributeList *attrList, uint length, byte [] data); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecKeychainItemCopyContent (IntPtr itemRef, ref SecItemClass itemClass, IntPtr attrList, ref uint length, ref IntPtr data); - - [DllImport (SecurityLibrary)] - static extern OSStatus SecKeychainItemFreeContent (IntPtr attrList, IntPtr data); - - #endregion - - static string GetError (OSStatus status) - { - CFString str = null; - - try { - str = new CFString (SecCopyErrorMessageString (status, IntPtr.Zero), true); - return str.ToString (); - } catch { - return status.ToString (); - } finally { - if (str != null) - str.Dispose (); - } - } - - /// - /// Gets a list of all certificates suitable for the given key usage. - /// - /// The matching certificates. - /// The key usage. - /// - /// The keychain has been disposed. - /// - public IList GetCertificates (CssmKeyUse keyUsage) - { - if (disposed) - throw new ObjectDisposedException ("SecKeychain"); - - var parser = new X509CertificateParser (); - var certs = new List (); - IntPtr searchRef, itemRef, certRef; - OSStatus status; - - status = SecIdentitySearchCreate (Handle, keyUsage, out searchRef); - if (status != OSStatus.Ok) - return certs; - - while (SecIdentitySearchCopyNext (searchRef, out itemRef) == OSStatus.Ok) { - if (SecIdentityCopyCertificate (itemRef, out certRef) == OSStatus.Ok) { - using (var data = new CFData (SecCertificateCopyData (certRef), true)) { - var rawData = data.GetBuffer (); - - try { - certs.Add (parser.ReadCertificate (rawData)); - } catch (CertificateException ex) { - Debug.WriteLine ("Failed to parse X509 certificate from keychain: {0}", ex); - } - } - } - - CFRelease (itemRef); - } - - CFRelease (searchRef); - - return certs; - } - - public IList GetAllCmsSigners () - { - if (disposed) - throw new ObjectDisposedException ("SecKeychain"); - - var signers = new List (); - IntPtr searchRef, itemRef, dataRef; - OSStatus status; - - status = SecIdentitySearchCreate (Handle, CssmKeyUse.Sign, out searchRef); - if (status != OSStatus.Ok) - return signers; - - while (SecIdentitySearchCopyNext (searchRef, out itemRef) == OSStatus.Ok) { - if (SecItemExport (itemRef, SecExternalFormat.PKCS12, SecItemImportExportFlags.None, IntPtr.Zero, out dataRef) == OSStatus.Ok) { - var data = new CFData (dataRef, true); - var rawData = data.GetBuffer (); - data.Dispose (); - - try { - using (var memory = new MemoryStream (rawData, false)) { - var pkcs12 = new Pkcs12Store (memory, new char[0]); - - foreach (string alias in pkcs12.Aliases) { - if (!pkcs12.IsKeyEntry (alias)) - continue; - - var chain = pkcs12.GetCertificateChain (alias); - var entry = pkcs12.GetKey (alias); - - signers.Add (new CmsSigner (chain, entry.Key)); - } - } - } catch (Exception ex) { - Debug.WriteLine ("Failed to decode keychain pkcs12 data: {0}", ex); - } - } - - CFRelease (itemRef); - } - - CFRelease (searchRef); - - return signers; - } - - public bool Add (AsymmetricKeyParameter key) - { - // FIXME: how do we convert an AsymmetricKeyParameter into something usable by MacOS? - throw new NotImplementedException (); - } - - public bool Add (X509Certificate certificate) - { - using (var cert = SecCertificate.Create (certificate.GetEncoded ())) { - var status = SecCertificateAddToKeychain (cert.Handle, Handle); - return status == OSStatus.Ok || status == OSStatus.DuplicateItem; - } - } - - public unsafe bool Contains (X509Certificate certificate) - { - if (certificate == null) - throw new ArgumentNullException ("certificate"); - - if (disposed) - throw new ObjectDisposedException ("SecKeychain"); - - // Note: we don't have to use an alias attribute, it's just that it might be faster to use it (fewer certificates we have to compare raw data for) - byte[] alias = Encoding.UTF8.GetBytes (certificate.GetCommonName ()); - IntPtr searchRef, itemRef; - bool found = false; - byte[] certData; - OSStatus status; - - fixed (byte* aliasPtr = alias) { - SecKeychainAttribute* attrs = stackalloc SecKeychainAttribute [1]; - int n = 0; - - if (alias != null) - attrs[n++] = new SecKeychainAttribute (SecItemAttr.Alias, (uint) alias.Length, (IntPtr) aliasPtr); - - SecKeychainAttributeList attrList = new SecKeychainAttributeList (n, (IntPtr) attrs); - - status = SecKeychainSearchCreateFromAttributes (Handle, SecItemClass.Certificate, &attrList, out searchRef); - if (status != OSStatus.Ok) - throw new Exception ("Could not enumerate certificates from the keychain. Error:\n" + GetError (status)); - - certData = certificate.GetEncoded (); - - while (!found && SecKeychainSearchCopyNext (searchRef, out itemRef) == OSStatus.Ok) { - SecItemClass itemClass = 0; - IntPtr data = IntPtr.Zero; - uint length = 0; - - status = SecKeychainItemCopyContent (itemRef, ref itemClass, IntPtr.Zero, ref length, ref data); - if (status == OSStatus.Ok) { - if (certData.Length == (int) length) { - byte[] rawData = new byte[(int) length]; - - Marshal.Copy (data, rawData, 0, (int) length); - - found = true; - for (int i = 0; i < rawData.Length; i++) { - if (rawData[i] != certData[i]) { - found = false; - break; - } - } - } - - SecKeychainItemFreeContent (IntPtr.Zero, data); - } - - CFRelease (itemRef); - } - - CFRelease (searchRef); - } - - return found; - } - -// public void ImportPkcs12 (byte[] rawData, string password) -// { -// if (rawData == null) -// throw new ArgumentNullException ("rawData"); -// -// if (password == null) -// throw new ArgumentNullException ("password"); -// -// if (disposed) -// throw new ObjectDisposedException ("SecKeychain"); -// -// using (var data = new CFData (rawData)) { -// var options = IntPtr.Zero; -// var items = IntPtr.Zero; -// -// var status = SecPKCS12Import (data.Handle, options, ref items); -// CFRelease (options); -// CFRelease (items); -// } -// } - - protected override void Dispose (bool disposing) - { - base.Dispose (disposing); - disposed = true; - } - } -} diff --git a/src/MimeKit/MacInterop/SecKeychainAttribute.cs b/src/MimeKit/MacInterop/SecKeychainAttribute.cs deleted file mode 100644 index b25a8d6..0000000 --- a/src/MimeKit/MacInterop/SecKeychainAttribute.cs +++ /dev/null @@ -1,45 +0,0 @@ -// -// SecKeychainAttribute.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - [StructLayout (LayoutKind.Sequential)] - struct SecKeychainAttribute - { - public SecItemAttr Tag; - public uint Length; - public IntPtr Data; - - public SecKeychainAttribute (SecItemAttr tag, uint length, IntPtr data) - { - Length = length; - Data = data; - Tag = tag; - } - } -} diff --git a/src/MimeKit/MacInterop/SecKeychainAttributeList.cs b/src/MimeKit/MacInterop/SecKeychainAttributeList.cs deleted file mode 100644 index 8b6b6c0..0000000 --- a/src/MimeKit/MacInterop/SecKeychainAttributeList.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// SecKeychainAttributeList.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.InteropServices; - -namespace MimeKit.MacInterop { - [StructLayout (LayoutKind.Sequential)] - struct SecKeychainAttributeList - { - public int Count; - public IntPtr Attrs; - - public SecKeychainAttributeList (int count, IntPtr attrs) - { - Count = count; - Attrs = attrs; - } - } -} diff --git a/src/MimeKit/MailboxAddress.cs b/src/MimeKit/MailboxAddress.cs deleted file mode 100644 index f8ab8d3..0000000 --- a/src/MimeKit/MailboxAddress.cs +++ /dev/null @@ -1,1098 +0,0 @@ -// -// MailboxAddress.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections.Generic; - -#if ENABLE_SNM -using System.Net.Mail; -#endif - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A mailbox address, as specified by rfc822. - /// - /// - /// Represents a mailbox address (commonly referred to as an email address) - /// for a single recipient. - /// - public class MailboxAddress : InternetAddress - { - string address; - int at; - - internal MailboxAddress (Encoding encoding, string name, IEnumerable route, string address, int at) : base (encoding, name) - { - Route = new DomainList (route); - Route.Changed += RouteChanged; - - this.address = address; - this.at = at; - } - - internal MailboxAddress (Encoding encoding, string name, string address, int at) : base (encoding, name) - { - Route = new DomainList (); - Route.Changed += RouteChanged; - - this.address = address; - this.at = at; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified name, address and route. The - /// specified text encoding is used when encoding the name according to the rules of rfc2047. - /// - /// The character encoding to be used for encoding the name. - /// The name of the mailbox. - /// The route of the mailbox. - /// The address of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is malformed. - /// - public MailboxAddress (Encoding encoding, string name, IEnumerable route, string address) : base (encoding, name) - { - if (address == null) - throw new ArgumentNullException (nameof (address)); - - Route = new DomainList (route); - Route.Changed += RouteChanged; - Address = address; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified name, address and route. - /// - /// The name of the mailbox. - /// The route of the mailbox. - /// The address of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is malformed. - /// - public MailboxAddress (string name, IEnumerable route, string address) : this (Encoding.UTF8, name, route, address) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified address and route. - /// - /// The route of the mailbox. - /// The address of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is malformed. - /// - [Obsolete ("This constructor will be going away. Use new MailboxAddress(string name, IEnumerable route, string address) instead.")] - public MailboxAddress (IEnumerable route, string address) : this (Encoding.UTF8, null, route, address) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified name and address. The - /// specified text encoding is used when encoding the name according to the rules of rfc2047. - /// - /// The character encoding to be used for encoding the name. - /// The name of the mailbox. - /// The address of the mailbox. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is malformed. - /// - public MailboxAddress (Encoding encoding, string name, string address) : base (encoding, name) - { - if (address == null) - throw new ArgumentNullException (nameof (address)); - - Route = new DomainList (); - Route.Changed += RouteChanged; - Address = address; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified name and address. - /// - /// The name of the mailbox. - /// The address of the mailbox. - /// - /// is null. - /// - /// - /// is malformed. - /// - public MailboxAddress (string name, string address) : this (Encoding.UTF8, name, address) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified address. - /// - /// The must be in the form user@example.com. - /// This method cannot be used to parse a free-form email address that includes - /// the name or encloses the address in angle brackets. - /// To parse a free-form email address, use instead. - /// - /// - /// The address of the mailbox. - /// - /// is null. - /// - /// - /// is malformed. - /// - [Obsolete("This constructor will be going away due to it causing too much confusion. Use new MailboxAddress(string name, string address) or MailboxAddress.Parse(string) instead.")] - public MailboxAddress (string address) : this (Encoding.UTF8, null, address) - { - } - - /// - /// Clone the mailbox address. - /// - /// - /// Clones the mailbox address. - /// - /// The cloned mailbox address. - public override InternetAddress Clone () - { - return new MailboxAddress (Encoding, Name, Route, Address); - } - - /// - /// Gets the mailbox route. - /// - /// - /// A route is convention that is rarely seen in modern email systems, but is supported - /// for compatibility with email archives. - /// - /// The mailbox route. - public DomainList Route { - get; private set; - } - - /// - /// Gets or sets the mailbox address. - /// - /// - /// Represents the actual email address and is in the form of user@domain.com. - /// - /// The mailbox address. - /// - /// is null. - /// - /// - /// is malformed. - /// - public string Address { - get { return address; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (value == address) - return; - - if (value.Length > 0) { - var buffer = CharsetUtils.UTF8.GetBytes (value); - int index = 0; - - TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], true, out string addrspec, out int atIndex); - - if (index != buffer.Length) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - address = addrspec; - at = atIndex; - } else { - address = string.Empty; - at = -1; - } - - OnChanged (); - } - } - - /// - /// Gets whether or not the address is an international address. - /// - /// - /// International addresses are addresses that contain international - /// characters in either their local-parts or their domains. - /// For more information, see section 3.2 of - /// rfc6532. - /// - /// true if the address is an international address; otherwise, false. - public bool IsInternational { - get { - if (address == null) - return false; - - if (ParseUtils.IsInternational (address)) - return true; - - foreach (var domain in Route) { - if (ParseUtils.IsInternational (domain)) - return true; - } - - return false; - } - } - - static string EncodeAddrspec (string addrspec, int at) - { - if (at != -1) { - var domain = addrspec.Substring (at + 1); - var local = addrspec.Substring (0, at); - - if (ParseUtils.IsInternational (local)) - local = ParseUtils.IdnEncode (local); - - if (ParseUtils.IsInternational (domain)) - domain = ParseUtils.IdnEncode (domain); - - return local + "@" + domain; - } - - return addrspec; - } - - /// - /// Encode an addrspec token according to IDN encoding rules. - /// - /// - /// Encodes an addrspec token according to IDN encoding rules. - /// - /// The encoded addrspec token. - /// The addrspec token. - /// - /// is null. - /// - public static string EncodeAddrspec (string addrspec) - { - if (addrspec == null) - throw new ArgumentNullException (nameof (addrspec)); - - if (addrspec.Length == 0) - return addrspec; - - var buffer = CharsetUtils.UTF8.GetBytes (addrspec); - int index = 0; - - if (!TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], false, out string address, out int at)) - return addrspec; - - return EncodeAddrspec (address, at); - } - - static string DecodeAddrspec (string addrspec, int at) - { - if (at != -1) { - var domain = addrspec.Substring (at + 1); - var local = addrspec.Substring (0, at); - - if (ParseUtils.IsIdnEncoded (local)) - local = ParseUtils.IdnDecode (local); - - if (ParseUtils.IsIdnEncoded (domain)) - domain = ParseUtils.IdnDecode (domain); - - return local + "@" + domain; - } - - return addrspec; - } - - /// - /// Decode an addrspec token according to IDN decoding rules. - /// - /// - /// Decodes an addrspec token according to IDN decoding rules. - /// - /// The decoded addrspec token. - /// The addrspec token. - /// - /// is null. - /// - public static string DecodeAddrspec (string addrspec) - { - if (addrspec == null) - throw new ArgumentNullException (nameof (addrspec)); - - if (addrspec.Length == 0) - return addrspec; - - var buffer = CharsetUtils.UTF8.GetBytes (addrspec); - int index = 0; - - if (!TryParseAddrspec (buffer, ref index, buffer.Length, new byte[0], false, out string address, out int at)) - return addrspec; - - return DecodeAddrspec (address, at); - } - - /// - /// Get the mailbox address, optionally encoded according to IDN encoding rules. - /// - /// - /// If is true, then the returned mailbox address will be encoded according to the IDN encoding rules. - /// - /// true if the address should be encoded according to IDN encoding rules; otherwise, false. - /// The mailbox address. - public string GetAddress (bool idnEncode) - { - if (idnEncode) - return EncodeAddrspec (address, at); - - return DecodeAddrspec (address, at); - } - - internal override void Encode (FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength) - { - var route = Route.Encode (options); - if (!string.IsNullOrEmpty (route)) - route += ":"; - - var addrspec = GetAddress (!options.International); - - if (!string.IsNullOrEmpty (Name)) { - string name; - - if (!options.International) { - var encoded = Rfc2047.EncodePhrase (options, Encoding, Name); - name = Encoding.ASCII.GetString (encoded, 0, encoded.Length); - } else { - name = EncodeInternationalizedPhrase (Name); - } - - if (lineLength + name.Length > options.MaxLineLength) { - if (name.Length > options.MaxLineLength) { - // we need to break up the name... - builder.AppendFolded (options, firstToken, name, ref lineLength); - } else { - // the name itself is short enough to fit on a single line, - // but only if we write it on a line by itself - if (!firstToken && lineLength > 1) { - builder.LineWrap (options); - lineLength = 1; - } - - lineLength += name.Length; - builder.Append (name); - } - } else { - // we can safely fit the name on this line... - lineLength += name.Length; - builder.Append (name); - } - - if ((lineLength + route.Length + addrspec.Length + 3) > options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ("\t<"); - lineLength = 2; - } else { - builder.Append (" <"); - lineLength += 2; - } - - lineLength += route.Length; - builder.Append (route); - - lineLength += addrspec.Length + 1; - builder.Append (addrspec); - builder.Append ('>'); - } else if (!string.IsNullOrEmpty (route)) { - if (!firstToken && (lineLength + route.Length + addrspec.Length + 2) > options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ("\t<"); - lineLength = 2; - } else { - builder.Append ('<'); - lineLength++; - } - - lineLength += route.Length; - builder.Append (route); - - lineLength += addrspec.Length + 1; - builder.Append (addrspec); - builder.Append ('>'); - } else { - if (!firstToken && (lineLength + addrspec.Length) > options.MaxLineLength) { - builder.LineWrap (options); - lineLength = 1; - } - - lineLength += addrspec.Length; - builder.Append (addrspec); - } - } - - /// - /// Returns a string representation of the , - /// optionally encoding it for transport. - /// - /// - /// Returns a string containing the formatted mailbox address. If the - /// parameter is true, then the mailbox name will be encoded according to the rules defined - /// in rfc2047, otherwise the name will not be encoded at all and will therefor only be suitable - /// for display purposes. - /// - /// A string representing the . - /// The formatting options. - /// If set to true, the will be encoded. - /// - /// is null. - /// - public override string ToString (FormatOptions options, bool encode) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (encode) { - var builder = new StringBuilder (); - int lineLength = 0; - - Encode (options, builder, true, ref lineLength); - - return builder.ToString (); - } - - string route = Route.ToString (); - if (!string.IsNullOrEmpty (route)) - route += ":"; - - if (!string.IsNullOrEmpty (Name)) - return MimeUtils.Quote (Name) + " <" + route + Address + ">"; - - if (!string.IsNullOrEmpty (route)) - return "<" + route + Address + ">"; - - return Address; - } - - #region IEquatable implementation - - /// - /// Determines whether the specified is equal to the current . - /// - /// - /// Compares two mailbox addresses to determine if they are identical or not. - /// - /// The to compare with the current . - /// true if the specified is equal to the current - /// ; otherwise, false. - public override bool Equals (InternetAddress other) - { - var mailbox = other as MailboxAddress; - - if (mailbox == null) - return false; - - return Name == mailbox.Name && Address == mailbox.Address; - } - - #endregion - - void RouteChanged (object sender, EventArgs e) - { - OnChanged (); - } - - internal static bool TryParse (ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out MailboxAddress mailbox) - { - var flags = AddressParserFlags.AllowMailboxAddress; - InternetAddress address; - - if (throwOnError) - flags |= AddressParserFlags.ThrowOnError; - - if (!TryParse (options, text, ref index, endIndex, 0, flags, out address)) { - mailbox = null; - return false; - } - - mailbox = (MailboxAddress) address; - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed mailbox address. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, int length, out MailboxAddress mailbox) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int endIndex = startIndex + length; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, false, out mailbox)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - mailbox = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// The parsed mailbox address. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - public static bool TryParse (byte[] buffer, int startIndex, int length, out MailboxAddress mailbox) - { - return TryParse (ParserOptions.Default, buffer, startIndex, length, out mailbox); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed mailbox address. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, int startIndex, out MailboxAddress mailbox) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int endIndex = buffer.Length; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, false, out mailbox)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - mailbox = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The starting index of the input buffer. - /// The parsed mailbox address. - /// - /// is null. - /// - /// - /// is out of range. - /// - public static bool TryParse (byte[] buffer, int startIndex, out MailboxAddress mailbox) - { - return TryParse (ParserOptions.Default, buffer, startIndex, out mailbox); - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The input buffer. - /// The parsed mailbox address. - /// - /// is null. - /// -or- - /// is null. - /// - public static bool TryParse (ParserOptions options, byte[] buffer, out MailboxAddress mailbox) - { - ParseUtils.ValidateArguments (options, buffer); - - int endIndex = buffer.Length; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, false, out mailbox)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - mailbox = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The input buffer. - /// The parsed mailbox address. - /// - /// is null. - /// - public static bool TryParse (byte[] buffer, out MailboxAddress mailbox) - { - return TryParse (ParserOptions.Default, buffer, out mailbox); - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The parser options to use. - /// The text. - /// The parsed mailbox address. - /// - /// is null. - /// - public static bool TryParse (ParserOptions options, string text, out MailboxAddress mailbox) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (text == null) - throw new ArgumentNullException (nameof (text)); - - var buffer = Encoding.UTF8.GetBytes (text); - int endIndex = buffer.Length; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, false, out mailbox)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, false) || index != endIndex) { - mailbox = null; - return false; - } - - return true; - } - - /// - /// Try to parse the given text into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// true, if the address was successfully parsed, false otherwise. - /// The text. - /// The parsed mailbox address. - /// - /// is null. - /// - public static bool TryParse (string text, out MailboxAddress mailbox) - { - return TryParse (ParserOptions.Default, text, out mailbox); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// could not be parsed. - /// - public static new MailboxAddress Parse (ParserOptions options, byte[] buffer, int startIndex, int length) - { - ParseUtils.ValidateArguments (options, buffer, startIndex, length); - - int endIndex = startIndex + length; - MailboxAddress mailbox; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, true, out mailbox)) - throw new ParseException ("No mailbox address found.", startIndex, startIndex); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return mailbox; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// The starting index of the input buffer. - /// The number of bytes in the input buffer to parse. - /// - /// is null. - /// - /// - /// and do not specify - /// a valid range in the byte array. - /// - /// - /// could not be parsed. - /// - public static new MailboxAddress Parse (byte[] buffer, int startIndex, int length) - { - return Parse (ParserOptions.Default, buffer, startIndex, length); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// The starting index of the input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is out of range. - /// - /// - /// could not be parsed. - /// - public static new MailboxAddress Parse (ParserOptions options, byte[] buffer, int startIndex) - { - ParseUtils.ValidateArguments (options, buffer, startIndex); - - int endIndex = buffer.Length; - MailboxAddress mailbox; - int index = startIndex; - - if (!TryParse (options, buffer, ref index, endIndex, true, out mailbox)) - throw new ParseException ("No mailbox address found.", startIndex, startIndex); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return mailbox; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// The starting index of the input buffer. - /// - /// is null. - /// - /// - /// is out of range. - /// - /// - /// could not be parsed. - /// - public static new MailboxAddress Parse (byte[] buffer, int startIndex) - { - return Parse (ParserOptions.Default, buffer, startIndex); - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The input buffer. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// could not be parsed. - /// - public static new MailboxAddress Parse (ParserOptions options, byte[] buffer) - { - ParseUtils.ValidateArguments (options, buffer); - - int endIndex = buffer.Length; - MailboxAddress mailbox; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, true, out mailbox)) - throw new ParseException ("No mailbox address found.", 0, 0); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return mailbox; - } - - /// - /// Parse the given input buffer into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// The parsed . - /// The input buffer. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public static new MailboxAddress Parse (byte[] buffer) - { - return Parse (ParserOptions.Default, buffer); - } - - /// - /// Parse the given text into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// The parsed . - /// The parser options to use. - /// The text. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// could not be parsed. - /// - public static new MailboxAddress Parse (ParserOptions options, string text) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (text == null) - throw new ArgumentNullException (nameof (text)); - - var buffer = Encoding.UTF8.GetBytes (text); - int endIndex = buffer.Length; - MailboxAddress mailbox; - int index = 0; - - if (!TryParse (options, buffer, ref index, endIndex, true, out mailbox)) - throw new ParseException ("No mailbox address found.", 0, 0); - - ParseUtils.SkipCommentsAndWhiteSpace (buffer, ref index, endIndex, true); - - if (index != endIndex) - throw new ParseException (string.Format ("Unexpected token at offset {0}", index), index, index); - - return mailbox; - } - - /// - /// Parse the given text into a new instance. - /// - /// - /// Parses a single . If the address is not a mailbox address or - /// there is more than a single mailbox address, then parsing will fail. - /// - /// The parsed . - /// The text. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public static new MailboxAddress Parse (string text) - { - return Parse (ParserOptions.Default, text); - } - -#if ENABLE_SNM - /// - /// Explicit cast to convert a to a - /// . - /// - /// - /// Casts a to a - /// in cases where you might want to make use of the System.Net.Mail APIs. - /// - /// The equivalent . - /// The mailbox. - public static explicit operator MailAddress (MailboxAddress mailbox) - { - return mailbox != null ? new MailAddress (mailbox.Address, mailbox.Name, mailbox.Encoding) : null; - } - - /// - /// Explicit cast to convert a - /// to a . - /// - /// - /// Casts a to a - /// in cases where you might want to make use of the the superior MimeKit APIs. - /// - /// The equivalent . - /// The mail address. - public static explicit operator MailboxAddress (MailAddress address) - { - return address != null ? new MailboxAddress (address.DisplayName, address.Address) : null; - } -#endif - } -} diff --git a/src/MimeKit/MessageDeliveryStatus.cs b/src/MimeKit/MessageDeliveryStatus.cs deleted file mode 100644 index 3495bdc..0000000 --- a/src/MimeKit/MessageDeliveryStatus.cs +++ /dev/null @@ -1,152 +0,0 @@ -// -// MessageDeliveryStatus.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.IO; - -namespace MimeKit { - /// - /// A message delivery status MIME part. - /// - /// - /// A message delivery status MIME part is a machine readable notification denoting the - /// delivery status of a message and has a MIME-type of message/delivery-status. - /// For more information, see rfc3464. - /// - /// - /// - /// - /// - public class MessageDeliveryStatus : MimePart - { - HeaderListCollection groups; - - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MessageDeliveryStatus (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public MessageDeliveryStatus () : base ("message", "delivery-status") - { - } - - /// - /// Get the groups of delivery status fields. - /// - /// - /// Gets the groups of delivery status fields. The first - /// contains the per-message fields while each remaining contains - /// fields that pertain to particular recipients of the message. - /// For more information about these fields and their values, check out - /// rfc3464. - /// Section 2.2 defines - /// the per-message fields while - /// Section 2.3 defines - /// the per-recipient fields. - /// - /// - /// - /// - /// The fields. - public HeaderListCollection StatusGroups { - get { - if (groups == null) { - if (Content == null) { - Content = new MimeContent (new MemoryBlockStream ()); - groups = new HeaderListCollection (); - } else { - groups = new HeaderListCollection (); - - using (var stream = Content.Open ()) { - var parser = new MimeParser (stream, MimeFormat.Entity); - - while (!parser.IsEndOfStream) { - var fields = parser.ParseHeaders (); - groups.Add (fields); - } - } - } - - groups.Changed += OnGroupsChanged; - } - - return groups; - } - } - - void OnGroupsChanged (object sender, EventArgs e) - { - var stream = new MemoryBlockStream (); - var options = FormatOptions.Default; - - for (int i = 0; i < groups.Count; i++) - groups[i].WriteTo (options, stream); - - stream.Position = 0; - - Content = new MimeContent (stream); - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMessageDeliveryStatus (this); - } - } -} diff --git a/src/MimeKit/MessageDispositionNotification.cs b/src/MimeKit/MessageDispositionNotification.cs deleted file mode 100644 index 975fc24..0000000 --- a/src/MimeKit/MessageDispositionNotification.cs +++ /dev/null @@ -1,129 +0,0 @@ -// -// MessageDispositionNotification.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.IO; - -namespace MimeKit { - /// - /// A message disposition notification MIME part. - /// - /// - /// A message disposition notification MIME part is a machine readable notification - /// denoting the disposition of a message once it has been successfully delivered - /// and has a MIME-type of message/disposition-notification. - /// - /// - public class MessageDispositionNotification : MimePart - { - HeaderList fields; - - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MessageDispositionNotification (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public MessageDispositionNotification () : base ("message", "disposition-notification") - { - } - - /// - /// Get the disposition notification fields. - /// - /// - /// Gets the disposition notification fields. - /// - /// The fields. - public HeaderList Fields { - get { - if (fields == null) { - if (Content == null) { - Content = new MimeContent (new MemoryBlockStream ()); - fields = new HeaderList (); - } else { - using (var stream = Content.Open ()) { - fields = HeaderList.Load (stream); - } - } - - fields.Changed += OnFieldsChanged; - } - - return fields; - } - } - - void OnFieldsChanged (object sender, HeaderListChangedEventArgs e) - { - var stream = new MemoryBlockStream (); - var options = FormatOptions.Default; - - fields.WriteTo (options, stream); - stream.Position = 0; - - Content = new MimeContent (stream); - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMessageDispositionNotification (this); - } - } -} diff --git a/src/MimeKit/MessageIdList.cs b/src/MimeKit/MessageIdList.cs deleted file mode 100644 index 52c435c..0000000 --- a/src/MimeKit/MessageIdList.cs +++ /dev/null @@ -1,378 +0,0 @@ -// -// MessageIdList.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A list of Message-Ids. - /// - /// - /// Used by the property. - /// - public class MessageIdList : IList - { - readonly List references; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new, empty, . - /// - public MessageIdList () - { - references = new List (); - } - - /// - /// Clones the . - /// - /// - /// Creates an exact copy of the . - /// - /// An exact copy of the . - public MessageIdList Clone () - { - var clone = new MessageIdList (); - - for (int i = 0; i < references.Count; i++) - clone.references.Add (references[i]); - - return clone; - } - - #region IList implementation - - /// - /// Get the index of the requested Message-Id, if it exists. - /// - /// - /// Finds the index of the specified Message-Id, if it exists. - /// - /// The index of the requested Message-Id; otherwise -1. - /// The Message-Id. - /// - /// is null. - /// - public int IndexOf (string messageId) - { - if (messageId == null) - throw new ArgumentNullException (nameof (messageId)); - - return references.IndexOf (messageId); - } - - static string ValidateMessageId (string messageId) - { - if (messageId.Length < 2 || messageId[0] != '<' || messageId[messageId.Length - 1] != '>') - return messageId; - - return messageId.Substring (1, messageId.Length - 2); - } - - /// - /// Insert the Message-Id at the specified index. - /// - /// - /// Inserts the Message-Id at the specified index in the list. - /// - /// The index to insert the Message-Id. - /// The Message-Id to insert. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void Insert (int index, string messageId) - { - if (messageId == null) - throw new ArgumentNullException (nameof (messageId)); - - references.Insert (index, ValidateMessageId (messageId)); - OnChanged (); - } - - /// - /// Remove the Message-Id at the specified index. - /// - /// - /// Removes the Message-Id at the specified index. - /// - /// The index. - /// - /// is out of range. - /// - public void RemoveAt (int index) - { - references.RemoveAt (index); - OnChanged (); - } - - /// - /// Get or set the Message-Id at the specified index. - /// - /// - /// Gets or sets the Message-Id at the specified index. - /// - /// The Message-Id at the specified index. - /// The index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public string this [int index] { - get { return references[index]; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (references[index] == value) - return; - - references[index] = ValidateMessageId (value); - OnChanged (); - } - } - - #endregion - - #region ICollection implementation - - /// - /// Add the specified Message-Id. - /// - /// - /// Adds the specified Message-Id to the end of the list. - /// - /// The Message-Id. - /// - /// is null. - /// - public void Add (string messageId) - { - if (messageId == null) - throw new ArgumentNullException (nameof (messageId)); - - references.Add (ValidateMessageId (messageId)); - OnChanged (); - } - - /// - /// Add a collection of Message-Id items. - /// - /// - /// Adds a collection of Message-Id items to append to the list. - /// - /// The Message-Id items to add. - /// - /// is null. - /// - public void AddRange (IEnumerable items) - { - if (items == null) - throw new ArgumentNullException (nameof (items)); - - foreach (var msgid in items) - references.Add (ValidateMessageId (msgid)); - - OnChanged (); - } - - /// - /// Clear the Message-Id list. - /// - /// - /// Removes all of the Message-Ids in the list. - /// - public void Clear () - { - references.Clear (); - OnChanged (); - } - - /// - /// Check if the contains the specified Message-Id. - /// - /// - /// Determines whether or not the list contains the specified Message-Id. - /// - /// true if the specified Message-Id is contained; - /// otherwise false. - /// The Message-Id. - /// - /// is null. - /// - public bool Contains (string messageId) - { - if (messageId == null) - throw new ArgumentNullException (nameof (messageId)); - - return references.Contains (messageId); - } - - /// - /// Copy all of the Message-Ids in the to the specified array. - /// - /// - /// Copies all of the Message-Ids within the into the array, - /// starting at the specified array index. - /// - /// The array to copy the Message-Ids to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (string[] array, int arrayIndex) - { - references.CopyTo (array, arrayIndex); - } - - /// - /// Remove a Message-Id from the . - /// - /// - /// Removes the first instance of the specified Message-Id from the list if it exists. - /// - /// true if the specified Message-Id was removed; - /// otherwise false. - /// The Message-Id. - /// - /// is null. - /// - public bool Remove (string messageId) - { - if (messageId == null) - throw new ArgumentNullException (nameof (messageId)); - - if (references.Remove (messageId)) { - OnChanged (); - return true; - } - - return false; - } - - /// - /// Get the number of Message-Ids in the . - /// - /// - /// Indicates the number of Message-Ids in the list. - /// - /// The number of Message-Ids. - public int Count { - get { return references.Count; } - } - - /// - /// Get a value indicating whether the is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the list of Message-Ids. - /// - /// - /// Gets an enumerator for the list of Message-Ids. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return references.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the list of Message-Ids. - /// - /// - /// Gets an enumerator for the list of Message-Ids. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return references.GetEnumerator (); - } - - #endregion - - /// - /// Serialize a to a string. - /// - /// - /// Each Message-Id will be surrounded by angle brackets. - /// If there are multiple Message-Ids in the list, they will be separated by whitespace. - /// - /// A string representing the . - public override string ToString () - { - var builder = new StringBuilder (); - - for (int i = 0; i < references.Count; i++) { - if (builder.Length > 0) - builder.Append (' '); - - builder.Append ('<'); - builder.Append (references[i]); - builder.Append ('>'); - } - - return builder.ToString (); - } - - internal event EventHandler Changed; - - void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - } -} diff --git a/src/MimeKit/MessageImportance.cs b/src/MimeKit/MessageImportance.cs deleted file mode 100644 index 24867ca..0000000 --- a/src/MimeKit/MessageImportance.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// MessageImportance.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit { - /// - /// An enumeration of message importance values. - /// - /// - /// Indicates the importance of a message. - /// - public enum MessageImportance { - /// - /// The message is of low importance. - /// - Low, - - /// - /// The message is of normal importance. - /// - Normal, - - /// - /// The message is of high importance. - /// - High - } -} diff --git a/src/MimeKit/MessagePart.cs b/src/MimeKit/MessagePart.cs deleted file mode 100644 index 763ad3f..0000000 --- a/src/MimeKit/MessagePart.cs +++ /dev/null @@ -1,288 +0,0 @@ -// -// MessagePart.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -using MimeKit.IO; - -namespace MimeKit { - /// - /// A MIME part containing a as its content. - /// - /// - /// Represents MIME entities such as those with a Content-Type of message/rfc822 or message/news. - /// - public class MessagePart : MimeEntity - { - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MessagePart (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The message subtype. - /// An array of initialization parameters: headers and message parts. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// contains more than one . - /// -or- - /// contains one or more arguments of an unknown type. - /// - public MessagePart (string subtype, params object[] args) : this (subtype) - { - if (args == null) - throw new ArgumentNullException (nameof (args)); - - MimeMessage message = null; - - foreach (object obj in args) { - if (obj == null || TryInit (obj)) - continue; - - if (obj is MimeMessage mesg) { - if (message != null) - throw new ArgumentException ("MimeMessage should not be specified more than once."); - - message = mesg; - continue; - } - - throw new ArgumentException ("Unknown initialization parameter: " + obj.GetType ()); - } - - if (message != null) - Message = message; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Initializes the based on the provided media type and subtype. - /// - /// The media type. - /// The media subtype. - /// - /// is null. - /// -or- - /// is null. - /// - protected MessagePart (string mediaType, string mediaSubtype) : base (mediaType, mediaSubtype) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new MIME message entity with the specified subtype. - /// - /// The message subtype. - /// - /// is null. - /// - public MessagePart (string subtype) : this ("message", subtype) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message/rfc822 MIME entity. - /// - public MessagePart () : this ("rfc822") - { - } - - /// - /// Gets or sets the message content. - /// - /// - /// Gets or sets the message content. - /// - /// The message content. - public MimeMessage Message { - get; set; - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMessagePart (this); - } - - /// - /// Prepare the MIME entity for transport using the specified encoding constraints. - /// - /// - /// Prepares the MIME entity for transport using the specified encoding constraints. - /// - /// The encoding constraint. - /// The maximum number of octets allowed per line (not counting the CRLF). Must be between 60 and 998 (inclusive). - /// - /// is not between 60 and 998 (inclusive). - /// -or- - /// is not a valid value. - /// - public override void Prepare (EncodingConstraint constraint, int maxLineLength = 78) - { - if (maxLineLength < FormatOptions.MinimumLineLength || maxLineLength > FormatOptions.MaximumLineLength) - throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - - if (Message != null) - Message.Prepare (constraint, maxLineLength); - } - - /// - /// Write the to the output stream. - /// - /// - /// Writes the MIME entity and its message to the output stream. - /// - /// The formatting options. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override void WriteTo (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - base.WriteTo (options, stream, contentOnly, cancellationToken); - - if (Message == null) - return; - - if (Message.MboxMarker != null && Message.MboxMarker.Length != 0) { - var cancellable = stream as ICancellableStream; - - if (cancellable != null) { - cancellable.Write (Message.MboxMarker, 0, Message.MboxMarker.Length, cancellationToken); - cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); - } else { - stream.Write (Message.MboxMarker, 0, Message.MboxMarker.Length); - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - } - - if (options.EnsureNewLine) { - options = options.Clone (); - options.EnsureNewLine = false; - } - - Message.WriteTo (options, stream, cancellationToken); - } - - /// - /// Asynchronously write the to the output stream. - /// - /// - /// Asynchronously writes the MIME entity and its message to the output stream. - /// - /// An awaitable task. - /// The formatting options. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task WriteToAsync (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - await base.WriteToAsync (options, stream, contentOnly, cancellationToken).ConfigureAwait (false); - - if (Message == null) - return; - - if (Message.MboxMarker != null && Message.MboxMarker.Length != 0) { - await stream.WriteAsync (Message.MboxMarker, 0, Message.MboxMarker.Length, cancellationToken).ConfigureAwait (false); - await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); - } - - if (options.EnsureNewLine) { - options = options.Clone (); - options.EnsureNewLine = false; - } - - await Message.WriteToAsync (options, stream, cancellationToken).ConfigureAwait (false); - } - } -} diff --git a/src/MimeKit/MessagePartial.cs b/src/MimeKit/MessagePartial.cs deleted file mode 100644 index 7598486..0000000 --- a/src/MimeKit/MessagePartial.cs +++ /dev/null @@ -1,511 +0,0 @@ -// -// MessagePartial.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -using MimeKit.IO; -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A MIME part containing a partial message as its content. - /// - /// - /// The "message/partial" MIME-type is used to split large messages into - /// multiple parts, typically to work around transport systems that have size - /// limitations (for example, some SMTP servers limit have a maximum message - /// size that they will accept). - /// - public class MessagePartial : MimePart - { - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MessagePartial (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new message/partial entity. - /// Three (3) parameters must be specified in the Content-Type header - /// of a message/partial: id, number, and total. - /// The "id" parameter is a unique identifier used to match the parts together. - /// The "number" parameter is the sequential (1-based) index of the partial message fragment. - /// The "total" parameter is the total number of pieces that make up the complete message. - /// - /// The id value shared among the partial message parts. - /// The (1-based) part number for this partial message part. - /// The total number of partial message parts. - /// - /// is null. - /// - /// - /// is less than 1. - /// -or- - /// is less than . - /// - public MessagePartial (string id, int number, int total) : base ("message", "partial") - { - if (id == null) - throw new ArgumentNullException (nameof (id)); - - if (number < 1) - throw new ArgumentOutOfRangeException (nameof (number)); - - if (total < number) - throw new ArgumentOutOfRangeException (nameof (total)); - - ContentType.Parameters.Add (new Parameter ("id", id)); - ContentType.Parameters.Add (new Parameter ("number", number.ToString ())); - ContentType.Parameters.Add (new Parameter ("total", total.ToString ())); - } - - /// - /// Gets the "id" parameter of the Content-Type header. - /// - /// - /// The "id" parameter is a unique identifier used to match the parts together. - /// - /// The identifier. - public string Id { - get { return ContentType.Parameters["id"]; } - } - - /// - /// Gets the "number" parameter of the Content-Type header. - /// - /// - /// The "number" parameter is the sequential (1-based) index of the partial message fragment. - /// - /// The part number. - public int? Number { - get { - var text = ContentType.Parameters["number"]; - int number; - - if (text == null || !int.TryParse (text, out number)) - return null; - - return number; - } - } - - /// - /// Gets the "total" parameter of the Content-Type header. - /// - /// - /// The "total" parameter is the total number of pieces that make up the complete message. - /// - /// The total number of parts. - public int? Total { - get { - var text = ContentType.Parameters["total"]; - int total; - - if (text == null || !int.TryParse (text, out total)) - return null; - - return total; - } - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMessagePartial (this); - } - - static MimeMessage CloneMessage (MimeMessage message) - { - var options = message.Headers.Options; - var clone = new MimeMessage (options); - - foreach (var header in message.Headers) - clone.Headers.Add (header.Clone ()); - - return clone; - } - - /// - /// Splits the specified message into multiple messages. - /// - /// - /// Splits the specified message into multiple messages, each with a - /// message/partial body no larger than the max size specified. - /// - /// An enumeration of partial messages. - /// The message. - /// The maximum size for each message body. - /// - /// is null. - /// - /// - /// is less than 1. - /// - public static IEnumerable Split (MimeMessage message, int maxSize) - { - if (message == null) - throw new ArgumentNullException (nameof (message)); - - if (maxSize < 1) - throw new ArgumentOutOfRangeException (nameof (maxSize)); - - var options = FormatOptions.CloneDefault (); - foreach (HeaderId id in Enum.GetValues (typeof (HeaderId))) { - switch (id) { - case HeaderId.Subject: - case HeaderId.MessageId: - case HeaderId.Encrypted: - case HeaderId.MimeVersion: - case HeaderId.ContentAlternative: - case HeaderId.ContentBase: - case HeaderId.ContentClass: - case HeaderId.ContentDescription: - case HeaderId.ContentDisposition: - case HeaderId.ContentDuration: - case HeaderId.ContentFeatures: - case HeaderId.ContentId: - case HeaderId.ContentIdentifier: - case HeaderId.ContentLanguage: - case HeaderId.ContentLength: - case HeaderId.ContentLocation: - case HeaderId.ContentMd5: - case HeaderId.ContentReturn: - case HeaderId.ContentTransferEncoding: - case HeaderId.ContentTranslationType: - case HeaderId.ContentType: - break; - default: - options.HiddenHeaders.Add (id); - break; - } - } - - var memory = new MemoryStream (); - - message.WriteTo (options, memory); - memory.Seek (0, SeekOrigin.Begin); - - if (memory.Length <= maxSize) { - memory.Dispose (); - - yield return message; - yield break; - } - - var streams = new List (); -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - var buf = memory.GetBuffer (); -#else - var buf = memory.ToArray (); -#endif - long startIndex = 0; - - while (startIndex < memory.Length) { - // Preferably, we'd split on whole-lines if we can, - // but if that's not possible, split on max size - long endIndex = Math.Min (memory.Length, startIndex + maxSize); - - if (endIndex < memory.Length) { - long ebx = endIndex; - - while (ebx > (startIndex + 1) && buf[ebx] != (byte) '\n') - ebx--; - - if (buf[ebx] == (byte) '\n') - endIndex = ebx + 1; - } - - streams.Add (new BoundStream (memory, startIndex, endIndex, true)); - startIndex = endIndex; - } - - var msgid = message.MessageId ?? MimeUtils.GenerateMessageId (); - int number = 1; - - foreach (var stream in streams) { - var part = new MessagePartial (msgid, number++, streams.Count) { - Content = new MimeContent (stream) - }; - - var submessage = CloneMessage (message); - submessage.MessageId = MimeUtils.GenerateMessageId (); - submessage.Body = part; - - yield return submessage; - } - - yield break; - } - - static int PartialCompare (MessagePartial partial1, MessagePartial partial2) - { - if (!partial1.Number.HasValue || !partial2.Number.HasValue || partial1.Id != partial2.Id) - throw new ArgumentException ("Partial messages have mismatching identifiers.", "partials"); - - return partial1.Number.Value - partial2.Number.Value; - } - - static void CombineHeaders (MimeMessage message, MimeMessage joined) - { - var headers = new List
(); - int i = 0; - - // RFC2046: Any header fields in the enclosed message which do not start with "Content-" - // (except for the "Subject", "Message-ID", "Encrypted", and "MIME-Version" fields) will - // be ignored and dropped. - while (i < joined.Headers.Count) { - var header = joined.Headers[i]; - - switch (header.Id) { - case HeaderId.Subject: - case HeaderId.MessageId: - case HeaderId.Encrypted: - case HeaderId.MimeVersion: - headers.Add (header); - header.Offset = null; - i++; - break; - default: - joined.Headers.RemoveAt (i); - break; - } - } - - // RFC2046: All of the header fields from the initial enclosing message, except - // those that start with "Content-" and the specific header fields "Subject", - // "Message-ID", "Encrypted", and "MIME-Version", must be copied, in order, - // to the new message. - i = 0; - foreach (var header in message.Headers) { - switch (header.Id) { - case HeaderId.Subject: - case HeaderId.MessageId: - case HeaderId.Encrypted: - case HeaderId.MimeVersion: - for (int j = 0; j < headers.Count; j++) { - if (headers[j].Id == header.Id) { - var original = headers[j]; - - joined.Headers.Remove (original); - joined.Headers.Insert (i++, original); - headers.RemoveAt (j); - break; - } - } - break; - default: - var clone = header.Clone (); - clone.Offset = null; - - joined.Headers.Insert (i++, clone); - break; - } - } - - if (joined.Body != null) { - foreach (var header in joined.Body.Headers) - header.Offset = null; - } - } - - static MimeMessage Join (ParserOptions options, MimeMessage message, IEnumerable partials, bool allowNullMessage) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (!allowNullMessage && message == null) - throw new ArgumentNullException (nameof (message)); - - if (partials == null) - throw new ArgumentNullException (nameof (partials)); - - var parts = partials.ToList (); - - if (parts.Count == 0) - return null; - - parts.Sort (PartialCompare); - - if (!parts[parts.Count - 1].Total.HasValue) - throw new ArgumentException ("The last partial does not have a Total.", nameof (partials)); - - int total = parts[parts.Count - 1].Total.Value; - if (parts.Count != total) - throw new ArgumentException ("The number of partials provided does not match the expected count.", nameof (partials)); - - string id = parts[0].Id; - - using (var chained = new ChainedStream ()) { - // chain all of the partial content streams... - for (int i = 0; i < parts.Count; i++) { - int number = parts[i].Number.Value; - - if (number != i + 1) - throw new ArgumentException ("One or more partials is missing.", nameof (partials)); - - var content = parts[i].Content; - - chained.Add (content.Open ()); - } - - var parser = new MimeParser (options, chained); - var joined = parser.ParseMessage (); - - if (message != null) - CombineHeaders (message, joined); - - return joined; - } - } - - /// - /// Joins the specified message/partial parts into the complete message. - /// - /// - /// Combines all of the message/partial fragments into its original, - /// complete, message. - /// - /// The re-combined message. - /// The parser options to use. - /// The message that contains the first `message/partial` part. - /// The list of partial message parts. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The last partial does not have a Total. - /// -or- - /// The number of partials provided does not match the expected count. - /// -or- - /// One or more partials is missing. - /// - public static MimeMessage Join (ParserOptions options, MimeMessage message, IEnumerable partials) - { - return Join (options, message, partials, false); - } - - /// - /// Joins the specified message/partial parts into the complete message. - /// - /// - /// Combines all of the message/partial fragments into its original, - /// complete, message. - /// - /// The re-combined message. - /// The message that contains the first `message/partial` part. - /// The list of partial message parts. - /// - /// is null. - /// -or- - /// is null. - /// - public static MimeMessage Join (MimeMessage message, IEnumerable partials) - { - return Join (ParserOptions.Default, message, partials, false); - } - - /// - /// Joins the specified message/partial parts into the complete message. - /// - /// - /// Combines all of the message/partial fragments into its original, - /// complete, message. - /// - /// The re-combined message. - /// The parser options to use. - /// The list of partial message parts. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The last partial does not have a Total. - /// -or- - /// The number of partials provided does not match the expected count. - /// -or- - /// One or more partials is missing. - /// - [Obsolete ("Use MessagePartial.Join (ParserOptions, MimeMessage, IEnumerable) instead.")] - public static MimeMessage Join (ParserOptions options, IEnumerable partials) - { - return Join (options, null, partials, true); - } - - /// - /// Joins the specified message/partial parts into the complete message. - /// - /// - /// Combines all of the message/partial fragments into its original, - /// complete, message. - /// - /// The re-combined message. - /// The list of partial message parts. - /// - /// is null. - /// - [Obsolete ("Use MessagePartial.Join (MimeMessage, IEnumerable) instead.")] - public static MimeMessage Join (IEnumerable partials) - { - return Join (ParserOptions.Default, null, partials, true); - } - } -} diff --git a/src/MimeKit/MessagePriority.cs b/src/MimeKit/MessagePriority.cs deleted file mode 100644 index e97261f..0000000 --- a/src/MimeKit/MessagePriority.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// MessagePriority.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit { - /// - /// An enumeration of message priority values. - /// - /// - /// Indicates the priority of a message. - /// - public enum MessagePriority { - /// - /// The message has non-urgent priority. - /// - NonUrgent, - - /// - /// The message has normal priority. - /// - Normal, - - /// - /// The message has urgent priority. - /// - Urgent - } -} diff --git a/src/MimeKit/MimeContent.cs b/src/MimeKit/MimeContent.cs deleted file mode 100644 index 27cffac..0000000 --- a/src/MimeKit/MimeContent.cs +++ /dev/null @@ -1,353 +0,0 @@ -// -// MimeContent.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -using MimeKit.IO; -using MimeKit.IO.Filters; - -namespace MimeKit { - /// - /// Encapsulates a content stream used by . - /// - /// - /// A represents the content of a . - /// The content has both a stream and an encoding (typically ). - /// - [Obsolete ("Use the MimeContent class instead.")] - public class ContentObject : MimeContent - { - /// - /// Initialize a new instance of the class. - /// - /// - /// When creating new s, the - /// should typically be unless the - /// has already been encoded. - /// - /// The content stream. - /// The stream encoding. - /// - /// is null. - /// - /// - /// does not support reading. - /// -or- - /// does not support seeking. - /// - [Obsolete ("Use the MimeContent class instead.")] - public ContentObject (Stream stream, ContentEncoding encoding = ContentEncoding.Default) : base (stream, encoding) {} - } - - /// - /// Encapsulates a content stream used by . - /// - /// - /// A represents the content of a . - /// The content has both a stream and an encoding (typically ). - /// - /// - /// - /// - public class MimeContent : IMimeContent - { - /// - /// Initialize a new instance of the class. - /// - /// - /// When creating new s, the - /// should typically be unless the - /// has already been encoded. - /// - /// The content stream. - /// The stream encoding. - /// - /// is null. - /// - /// - /// does not support reading. - /// -or- - /// does not support seeking. - /// - public MimeContent (Stream stream, ContentEncoding encoding = ContentEncoding.Default) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (!stream.CanRead) - throw new ArgumentException ("The stream does not support reading.", nameof (stream)); - - if (!stream.CanSeek) - throw new ArgumentException ("The stream does not support seeking.", nameof (stream)); - - Encoding = encoding; - Stream = stream; - } - - #region IContentObject implementation - - /// - /// Get or set the content encoding. - /// - /// - /// If the was parsed from an existing stream, the - /// encoding will be identical to the , - /// otherwise it will typically be . - /// - /// The content encoding. - public ContentEncoding Encoding { - get; private set; - } - - /// - /// Get the new-line format, if known. - /// - /// - /// This property is typically only set by the as it parses - /// the content of a and is only used as a hint when verifying - /// digital signatures. - /// - /// The new-line format, if known. - public NewLineFormat? NewLineFormat { get; set; } - - /// - /// Get the content stream. - /// - /// - /// Gets the content stream. - /// - /// The stream. - public Stream Stream { - get; private set; - } - - /// - /// Open the decoded content stream. - /// - /// - /// Provides a means of reading the decoded content without having to first write it to another - /// stream using . - /// - /// The decoded content stream. - public Stream Open () - { - Stream.Seek (0, SeekOrigin.Begin); - - var filtered = new FilteredStream (Stream); - filtered.Add (DecoderFilter.Create (Encoding)); - - return filtered; - } - - /// - /// Copy the content stream to the specified output stream. - /// - /// - /// This is equivalent to simply using - /// to copy the content stream to the output stream except that this method is cancellable. - /// If you want the decoded content, use - /// instead. - /// - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var readable = Stream as ICancellableStream; - var writable = stream as ICancellableStream; - var buf = new byte[4096]; - int nread; - - Stream.Seek (0, SeekOrigin.Begin); - - try { - do { - if (readable != null) { - if ((nread = readable.Read (buf, 0, buf.Length, cancellationToken)) <= 0) - break; - } else { - cancellationToken.ThrowIfCancellationRequested (); - if ((nread = Stream.Read (buf, 0, buf.Length)) <= 0) - break; - } - - if (writable != null) { - writable.Write (buf, 0, nread, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (buf, 0, nread); - } - } while (true); - - Stream.Seek (0, SeekOrigin.Begin); - } catch (OperationCanceledException) { - // try and reset the stream - try { - Stream.Seek (0, SeekOrigin.Begin); - } catch (IOException) { - } - - throw; - } - } - - /// - /// Asynchronously copy the content stream to the specified output stream. - /// - /// - /// This is equivalent to simply using - /// to copy the content stream to the output stream except that this method is cancellable. - /// If you want the decoded content, use - /// instead. - /// - /// An awaitable task. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public async Task WriteToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var buf = new byte[4096]; - int nread; - - Stream.Seek (0, SeekOrigin.Begin); - - try { - do { - if ((nread = await Stream.ReadAsync (buf, 0, buf.Length, cancellationToken).ConfigureAwait (false)) <= 0) - break; - - await stream.WriteAsync (buf, 0, nread, cancellationToken).ConfigureAwait (false); - } while (true); - - Stream.Seek (0, SeekOrigin.Begin); - } catch (OperationCanceledException) { - // try and reset the stream - try { - Stream.Seek (0, SeekOrigin.Begin); - } catch (IOException) { - } - - throw; - } - } - - /// - /// Decode the content stream into another stream. - /// - /// - /// If the content stream is encoded, this method will decode it into the output stream - /// using a suitable decoder based on the property, otherwise the - /// stream will be copied into the output stream as-is. - /// - /// - /// - /// - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void DecodeTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - using (var filtered = new FilteredStream (stream)) { - filtered.Add (DecoderFilter.Create (Encoding)); - WriteTo (filtered, cancellationToken); - filtered.Flush (cancellationToken); - } - } - - /// - /// Asynchronously decode the content stream into another stream. - /// - /// - /// If the content stream is encoded, this method will decode it into the output stream - /// using a suitable decoder based on the property, otherwise the - /// stream will be copied into the output stream as-is. - /// - /// - /// - /// - /// An awaitable task. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public async Task DecodeToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - using (var filtered = new FilteredStream (stream)) { - filtered.Add (DecoderFilter.Create (Encoding)); - await WriteToAsync (filtered, cancellationToken).ConfigureAwait (false); - await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); - } - } - - #endregion - } -} diff --git a/src/MimeKit/MimeEntity.cs b/src/MimeKit/MimeEntity.cs deleted file mode 100644 index a654c28..0000000 --- a/src/MimeKit/MimeEntity.cs +++ /dev/null @@ -1,1732 +0,0 @@ -// -// MimeEntity.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit.IO; -using MimeKit.Utils; - -namespace MimeKit { - /// - /// An abstract MIME entity. - /// - /// - /// A MIME entity is really just a node in a tree structure of MIME parts in a MIME message. - /// There are 3 basic types of entities: , , - /// and (which is actually just a special variation of - /// who's content is another MIME message/document). All other types are - /// derivatives of one of those. - /// - public abstract class MimeEntity - { - internal bool EnsureNewLine; - ContentDisposition disposition; - string contentId; - Uri location; - Uri baseUri; - - /// - /// Initialize a new instance of the class - /// based on the . - /// - /// - /// Custom subclasses MUST implement this constructor - /// in order to register it using . - /// - /// Information used by the constructor. - /// - /// is null. - /// - protected MimeEntity (MimeEntityConstructorArgs args) - { - if (args == null) - throw new ArgumentNullException (nameof (args)); - - Headers = new HeaderList (args.ParserOptions); - ContentType = args.ContentType; - - ContentType.Changed += ContentTypeChanged; - Headers.Changed += HeadersChanged; - - foreach (var header in args.Headers) { - if (args.IsTopLevel && !header.Field.StartsWith ("Content-", StringComparison.OrdinalIgnoreCase)) - continue; - - Headers.Add (header); - } - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Initializes the based on the provided media type and subtype. - /// - /// The media type. - /// The media subtype. - /// - /// is null. - /// -or- - /// is null. - /// - protected MimeEntity (string mediaType, string mediaSubtype) : this (new ContentType (mediaType, mediaSubtype)) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Initializes the to the one provided. - /// - /// The content type. - /// - /// is null. - /// - protected MimeEntity (ContentType contentType) - { - if (contentType == null) - throw new ArgumentNullException (nameof (contentType)); - - Headers = new HeaderList (); - ContentType = contentType; - - ContentType.Changed += ContentTypeChanged; - Headers.Changed += HeadersChanged; - - SerializeContentType (); - } - - /// - /// Tries to use the given object to initialize the appropriate property. - /// - /// - /// Initializes the appropriate property based on the type of the object. - /// - /// The object. - /// true if the object was recognized and used; false otherwise. - protected bool TryInit (object obj) - { - // The base MimeEntity class only knows about Headers. - if (obj is Header header) { - Headers.Add (header); - return true; - } - - if (obj is IEnumerable
headers) { - foreach (Header h in headers) - Headers.Add (h); - return true; - } - - return false; - } - - /// - /// Gets the list of headers. - /// - /// - /// Represents the list of headers for a MIME part. Typically, the headers of - /// a MIME part will be various Content-* headers such as Content-Type or - /// Content-Disposition, but may include just about anything. - /// - /// The list of headers. - public HeaderList Headers { - get; private set; - } - - /// - /// Gets or sets the content disposition. - /// - /// - /// Represents the pre-parsed Content-Disposition header value, if present. - /// If the Content-Disposition header is not set, then this property will - /// be null. - /// - /// The content disposition. - public ContentDisposition ContentDisposition { - get { return disposition; } - set { - if (disposition == value) - return; - - if (disposition != null) { - disposition.Changed -= ContentDispositionChanged; - RemoveHeader ("Content-Disposition"); - } - - disposition = value; - if (disposition != null) { - disposition.Changed += ContentDispositionChanged; - SerializeContentDisposition (); - } - } - } - - /// - /// Gets the type of the content. - /// - /// - /// The Content-Type header specifies information about the type of content contained - /// within the MIME entity. - /// - /// The type of the content. - public ContentType ContentType { - get; private set; - } - - /// - /// Gets or sets the base content URI. - /// - /// - /// The Content-Base header specifies the base URI for the - /// in cases where the is a relative URI. - /// The Content-Base URI must be an absolute URI. - /// For more information, see rfc2110. - /// - /// The base content URI or null. - /// - /// is not an absolute URI. - /// - public Uri ContentBase { - get { return baseUri; } - set { - if (baseUri == value) - return; - - if (value != null && !value.IsAbsoluteUri) - throw new ArgumentException ("The Content-Base URI may only be set to an absolute URI.", nameof (value)); - - baseUri = value; - - if (value != null) - SetHeader ("Content-Base", value.ToString ()); - else - RemoveHeader ("Content-Base"); - } - } - - /// - /// Gets or sets the content location. - /// - /// - /// The Content-Location header specifies the URI for a MIME entity and can be - /// either absolute or relative. - /// Setting a Content-Location URI allows other objects - /// within the same multipart/related container to reference this part by URI. This - /// can be useful, for example, when constructing an HTML message body that needs to - /// reference image attachments. - /// For more information, see rfc2110. - /// - /// The content location or null. - public Uri ContentLocation { - get { return location; } - set { - if (location == value) - return; - - location = value; - - if (value != null) - SetHeader ("Content-Location", value.ToString ()); - else - RemoveHeader ("Content-Location"); - } - } - - /// - /// Gets or sets the content identifier. - /// - /// - /// The Content-Id header is used for uniquely identifying a particular entity and - /// uses the same syntax as the Message-Id header on MIME messages. - /// Setting a Content-Id allows other objects within the same - /// multipart/related container to reference this part by its unique identifier, typically - /// by using a "cid:" URI in an HTML-formatted message body. This can be useful, for example, - /// when the HTML-formatted message body needs to reference image attachments. - /// - /// The content identifier. - public string ContentId { - get { return contentId; } - set { - if (contentId == value) - return; - - if (value == null) { - RemoveHeader ("Content-Id"); - contentId = null; - return; - } - - var buffer = Encoding.UTF8.GetBytes (value); - int index = 0; - - if (!ParseUtils.TryParseMsgId (buffer, ref index, buffer.Length, false, false, out string id)) - throw new ArgumentException ("Invalid Content-Id format.", nameof (value)); - - contentId = id; - - SetHeader ("Content-Id", "<" + contentId + ">"); - } - } - - /// - /// Gets a value indicating whether this is an attachment. - /// - /// - /// If the Content-Disposition header is set and has a value of "attachment", - /// then this property returns true. Otherwise it is assumed that the - /// is not meant to be treated as an attachment. - /// - /// true if this is an attachment; otherwise, false. - public bool IsAttachment { - get { return ContentDisposition != null && ContentDisposition.IsAttachment; } - set { - if (value) { - if (ContentDisposition == null) - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment); - else if (!ContentDisposition.IsAttachment) - ContentDisposition.Disposition = ContentDisposition.Attachment; - } else if (ContentDisposition != null && ContentDisposition.IsAttachment) { - ContentDisposition.Disposition = ContentDisposition.Inline; - } - } - } - - /// - /// Returns a that represents the for debugging purposes. - /// - /// - /// Returns a that represents the for debugging purposes. - /// In general, the string returned from this method SHOULD NOT be used for serializing - /// the entity to disk. It is recommended that you use instead. - /// If this method is used for serializing the entity to disk, the iso-8859-1 text encoding should be used for - /// conversion. - /// - /// A that represents the for debugging purposes. - public override string ToString () - { - using (var memory = new MemoryStream ()) { - WriteTo (memory); - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - var buffer = memory.GetBuffer (); -#else - var buffer = memory.ToArray (); -#endif - int count = (int) memory.Length; - - return CharsetUtils.Latin1.GetString (buffer, 0, count); - } - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public virtual void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMimeEntity (this); - } - - /// - /// Prepare the MIME entity for transport using the specified encoding constraints. - /// - /// - /// Prepares the MIME entity for transport using the specified encoding constraints. - /// - /// The encoding constraint. - /// The maximum allowable length for a line (not counting the CRLF). Must be between 72 and 998 (inclusive). - /// - /// is not between 72 and 998 (inclusive). - /// -or- - /// is not a valid value. - /// - public abstract void Prepare (EncodingConstraint constraint, int maxLineLength = 78); - - /// - /// Write the to the specified output stream. - /// - /// - /// Writes the headers to the output stream, followed by a blank line. - /// Subclasses should override this method to write the content of the entity. - /// - /// The formatting options. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public virtual void WriteTo (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (!contentOnly) - Headers.WriteTo (options, stream, cancellationToken); - } - - /// - /// Asynchronously write the to the specified output stream. - /// - /// - /// Asynchronously writes the headers to the output stream, followed by a blank line. - /// Subclasses should override this method to write the content of the entity. - /// - /// An awaitable task. - /// The formatting options. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public virtual Task WriteToAsync (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (!contentOnly) - return Headers.WriteToAsync (options, stream, cancellationToken); - - return Task.FromResult (0); - } - - /// - /// Write the to the specified output stream. - /// - /// - /// Writes the headers to the output stream, followed by a blank line. - /// Subclasses should override this method to write the content of the entity. - /// - /// The formatting options. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (options, stream, false, cancellationToken); - } - - /// - /// Asynchronously write the to the specified output stream. - /// - /// - /// Asynchronously writes the headers to the output stream, followed by a blank line. - /// Subclasses should override this method to write the content of the entity. - /// - /// An awaitable task. - /// The formatting options. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (options, stream, false, cancellationToken); - } - - /// - /// Write the to the specified output stream. - /// - /// - /// Writes the entity to the output stream. - /// - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (FormatOptions.Default, stream, contentOnly, cancellationToken); - } - - /// - /// Asynchronously write the to the specified output stream. - /// - /// - /// Asynchronously writes the entity to the output stream. - /// - /// An awaitable task. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (FormatOptions.Default, stream, contentOnly, cancellationToken); - } - - /// - /// Write the to the specified output stream. - /// - /// - /// Writes the entity to the output stream. - /// - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (FormatOptions.Default, stream, false, cancellationToken); - } - - /// - /// Asynchronously write the to the specified output stream. - /// - /// - /// Asynchronously writes the entity to the output stream. - /// - /// An awaitable task. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (FormatOptions.Default, stream, false, cancellationToken); - } - - /// - /// Write the to the specified file. - /// - /// - /// Writes the entity to the specified file using the provided formatting options. - /// - /// The formatting options. - /// The file. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (FormatOptions options, string fileName, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) - WriteTo (options, stream, contentOnly, cancellationToken); - } - - /// - /// Asynchronously write the to the specified file. - /// - /// - /// Asynchronously writes the entity to the specified file using the provided formatting options. - /// - /// An awaitable task. - /// The formatting options. - /// The file. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public async Task WriteToAsync (FormatOptions options, string fileName, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) - await WriteToAsync (options, stream, contentOnly, cancellationToken).ConfigureAwait (false); - } - - /// - /// Write the to the specified file. - /// - /// - /// Writes the entity to the specified file using the provided formatting options. - /// - /// The formatting options. - /// The file. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (FormatOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) { - WriteTo (options, stream, false, cancellationToken); - stream.Flush (); - } - } - - /// - /// Asynchronously write the to the specified file. - /// - /// - /// Asynchronously writes the entity to the specified file using the provided formatting options. - /// - /// An awaitable task. - /// The formatting options. - /// The file. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public async Task WriteToAsync (FormatOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) { - await WriteToAsync (options, stream, false, cancellationToken).ConfigureAwait (false); - await stream.FlushAsync (cancellationToken).ConfigureAwait (false); - } - } - - /// - /// Write the to the specified file. - /// - /// - /// Writes the entity to the specified file using the default formatting options. - /// - /// The file. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (string fileName, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (FormatOptions.Default, fileName, contentOnly, cancellationToken); - } - - /// - /// Asynchronously write the to the specified file. - /// - /// - /// Asynchronously writes the entity to the specified file using the default formatting options. - /// - /// An awaitable task. - /// The file. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (string fileName, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (FormatOptions.Default, fileName, contentOnly, cancellationToken); - } - - /// - /// Write the to the specified file. - /// - /// - /// Writes the entity to the specified file using the default formatting options. - /// - /// The file. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (FormatOptions.Default, fileName, cancellationToken); - } - - /// - /// Asynchronously write the to the specified file. - /// - /// - /// Asynchronously writes the entity to the specified file using the default formatting options. - /// - /// An awaitable task. - /// The file. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (FormatOptions.Default, fileName, cancellationToken); - } - - /// - /// Remove a header by name. - /// - /// - /// Removes all headers matching the specified name without - /// calling . - /// - /// The name of the header. - protected void RemoveHeader (string name) - { - Headers.Changed -= HeadersChanged; - - try { - Headers.RemoveAll (name); - } finally { - Headers.Changed += HeadersChanged; - } - } - - /// - /// Set the value of a header. - /// - /// - /// Sets the header to the specified value without - /// calling . - /// - /// The name of the header. - /// The value of the header. - protected void SetHeader (string name, string value) - { - Headers.Changed -= HeadersChanged; - - try { - Headers[name] = value; - } finally { - Headers.Changed += HeadersChanged; - } - } - - /// - /// Set the value of a header using the raw value. - /// - /// - /// Sets the header to the specified value without - /// calling . - /// - /// The name of the header. - /// The raw value of the header. - protected void SetHeader (string name, byte[] rawValue) - { - var header = new Header (Headers.Options, name.ToHeaderId (), name, rawValue); - - Headers.Changed -= HeadersChanged; - - try { - Headers.Replace (header); - } finally { - Headers.Changed += HeadersChanged; - } - } - - void SerializeContentDisposition () - { - var text = disposition.Encode (FormatOptions.Default, Encoding.UTF8); - var raw = Encoding.UTF8.GetBytes (text); - - SetHeader ("Content-Disposition", raw); - } - - void SerializeContentType () - { - var text = ContentType.Encode (FormatOptions.Default, Encoding.UTF8); - var raw = Encoding.UTF8.GetBytes (text); - - SetHeader ("Content-Type", raw); - } - - void ContentDispositionChanged (object sender, EventArgs e) - { - SerializeContentDisposition (); - } - - void ContentTypeChanged (object sender, EventArgs e) - { - SerializeContentType (); - } - - /// - /// Called when the headers change in some way. - /// - /// - /// Whenever a header is added, changed, or removed, this method will - /// be called in order to allow custom subclasses - /// to update their state. - /// Overrides of this method should call the base method so that their - /// superclass may also update its own state. - /// - /// The type of change. - /// The header being added, changed or removed. - protected virtual void OnHeadersChanged (HeaderListChangedAction action, Header header) - { - int index = 0; - string text; - - switch (action) { - case HeaderListChangedAction.Added: - case HeaderListChangedAction.Changed: - switch (header.Id) { - case HeaderId.ContentDisposition: - if (disposition != null) - disposition.Changed -= ContentDispositionChanged; - - if (ContentDisposition.TryParse (Headers.Options, header.RawValue, out disposition)) - disposition.Changed += ContentDispositionChanged; - break; - case HeaderId.ContentLocation: - text = header.Value.Trim (); - - if (Uri.IsWellFormedUriString (text, UriKind.Absolute)) - location = new Uri (text, UriKind.Absolute); - else if (Uri.IsWellFormedUriString (text, UriKind.Relative)) - location = new Uri (text, UriKind.Relative); - else - location = null; - break; - case HeaderId.ContentBase: - text = header.Value.Trim (); - - if (Uri.IsWellFormedUriString (text, UriKind.Absolute)) - baseUri = new Uri (text, UriKind.Absolute); - else - baseUri = null; - break; - case HeaderId.ContentId: - if (ParseUtils.TryParseMsgId (header.RawValue, ref index, header.RawValue.Length, false, false, out string msgid)) - contentId = msgid; - else - contentId = null; - break; - } - break; - case HeaderListChangedAction.Removed: - switch (header.Id) { - case HeaderId.ContentDisposition: - if (disposition != null) - disposition.Changed -= ContentDispositionChanged; - - disposition = null; - break; - case HeaderId.ContentLocation: - location = null; - break; - case HeaderId.ContentBase: - baseUri = null; - break; - case HeaderId.ContentId: - contentId = null; - break; - } - break; - case HeaderListChangedAction.Cleared: - if (disposition != null) - disposition.Changed -= ContentDispositionChanged; - - disposition = null; - contentId = null; - location = null; - baseUri = null; - break; - default: - throw new ArgumentOutOfRangeException (nameof (action)); - } - } - - void HeadersChanged (object sender, HeaderListChangedEventArgs e) - { - OnHeadersChanged (e.Action, e.Header); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save mmeory usage, but also improve - /// performance. - /// - /// The parsed MIME entity. - /// The parser options. - /// The stream. - /// true if the stream is persistent; otherwise false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeEntity Load (ParserOptions options, Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new MimeParser (options, stream, MimeFormat.Entity, persistent); - - return parser.ParseEntity (cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save mmeory usage, but also improve - /// performance. - /// - /// The parsed MIME entity. - /// The parser options. - /// The stream. - /// true if the stream is persistent; otherwise false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (ParserOptions options, Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new MimeParser (options, stream, MimeFormat.Entity, persistent); - - return parser.ParseEntityAsync (cancellationToken); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// - /// The parsed MIME entity. - /// The parser options. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeEntity Load (ParserOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (options, stream, false, cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// - /// The parsed MIME entity. - /// The parser options. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (ParserOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (options, stream, false, cancellationToken); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save mmeory usage, but also improve - /// performance. - /// - /// The parsed MIME entity. - /// The stream. - /// true if the stream is persistent; otherwise false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeEntity Load (Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, stream, persistent, cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save mmeory usage, but also improve - /// performance. - /// - /// The parsed MIME entity. - /// The stream. - /// true if the stream is persistent; otherwise false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, stream, persistent, cancellationToken); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// - /// The parsed MIME entity. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeEntity Load (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, stream, false, cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// - /// The parsed MIME entity. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, stream, false, cancellationToken); - } - - /// - /// Load a from the specified file. - /// - /// - /// Loads a from the file at the give file path, - /// using the specified . - /// - /// The parsed entity. - /// The parser options. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeEntity Load (ParserOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - return Load (options, stream, cancellationToken); - } - - /// - /// Asynchronously load a from the specified file. - /// - /// - /// Loads a from the file at the give file path, - /// using the specified . - /// - /// The parsed entity. - /// The parser options. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static async Task LoadAsync (ParserOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - return await LoadAsync (options, stream, cancellationToken).ConfigureAwait (false); - } - - /// - /// Load a from the specified file. - /// - /// - /// Loads a from the file at the give file path, - /// using the default . - /// - /// The parsed entity. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeEntity Load (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, fileName, cancellationToken); - } - - /// - /// Asynchroinously load a from the specified file. - /// - /// - /// Loads a from the file at the give file path, - /// using the default . - /// - /// The parsed entity. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, fileName, cancellationToken); - } - - /// - /// Load a from the specified content stream. - /// - /// - /// This method is mostly meant for use with APIs such as - /// where the headers are parsed separately from the content. - /// - /// The parsed MIME entity. - /// The parser options. - /// The Content-Type of the stream. - /// The content stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeEntity Load (ParserOptions options, ContentType contentType, Stream content, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (contentType == null) - throw new ArgumentNullException (nameof (contentType)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var format = FormatOptions.CloneDefault (); - format.NewLineFormat = NewLineFormat.Dos; - - var encoded = contentType.Encode (format, Encoding.UTF8); - var header = string.Format ("Content-Type:{0}\r\n", encoded); - var chained = new ChainedStream (); - - chained.Add (new MemoryStream (Encoding.UTF8.GetBytes (header), false)); - chained.Add (content); - - return Load (options, chained, cancellationToken); - } - - /// - /// Asynchronously load a from the specified content stream. - /// - /// - /// This method is mostly meant for use with APIs such as - /// where the headers are parsed separately from the content. - /// - /// The parsed MIME entity. - /// The parser options. - /// The Content-Type of the stream. - /// The content stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (ParserOptions options, ContentType contentType, Stream content, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (contentType == null) - throw new ArgumentNullException (nameof (contentType)); - - if (content == null) - throw new ArgumentNullException (nameof (content)); - - var format = FormatOptions.CloneDefault (); - format.NewLineFormat = NewLineFormat.Dos; - - var encoded = contentType.Encode (format, Encoding.UTF8); - var header = string.Format ("Content-Type:{0}\r\n", encoded); - var chained = new ChainedStream (); - - chained.Add (new MemoryStream (Encoding.UTF8.GetBytes (header), false)); - chained.Add (content); - - return LoadAsync (options, chained, cancellationToken); - } - - /// - /// Load a from the specified content stream. - /// - /// - /// This method is mostly meant for use with APIs such as - /// where the headers are parsed separately from the content. - /// - /// - /// - /// - /// The parsed MIME entity. - /// The Content-Type of the stream. - /// The content stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeEntity Load (ContentType contentType, Stream content, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, contentType, content, cancellationToken); - } - - /// - /// Asynchronously load a from the specified content stream. - /// - /// - /// This method is mostly meant for use with APIs such as - /// where the headers are parsed separately from the content. - /// - /// The parsed MIME entity. - /// The Content-Type of the stream. - /// The content stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (ContentType contentType, Stream content, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, contentType, content, cancellationToken); - } - } -} diff --git a/src/MimeKit/MimeEntityConstructorArgs.cs b/src/MimeKit/MimeEntityConstructorArgs.cs deleted file mode 100644 index e2896bc..0000000 --- a/src/MimeKit/MimeEntityConstructorArgs.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// MimeEntityConstructorArgs.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.Collections.Generic; - -namespace MimeKit { - /// - /// MIME entity constructor arguments. - /// - /// - /// MIME entity constructor arguments. - /// - public sealed class MimeEntityConstructorArgs - { - internal readonly ParserOptions ParserOptions; - internal readonly IEnumerable
Headers; - internal readonly ContentType ContentType; - internal readonly bool IsTopLevel; - - internal MimeEntityConstructorArgs (ParserOptions options, ContentType ctype, IEnumerable
headers, bool toplevel) - { - ParserOptions = options; - IsTopLevel = toplevel; - ContentType = ctype; - Headers = headers; - } - } -} diff --git a/src/MimeKit/MimeFormat.cs b/src/MimeKit/MimeFormat.cs deleted file mode 100644 index 30a1692..0000000 --- a/src/MimeKit/MimeFormat.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// MimeFormat.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit { - /// - /// The format of the MIME stream. - /// - /// - /// The format of the MIME stream. - /// - public enum MimeFormat : byte { - /// - /// The stream contains a single MIME entity or message. - /// - Entity, - - /// - /// The stream is in the Unix mbox format and may contain - /// more than a single message. - /// - Mbox, - - /// - /// The default stream format. - /// - Default = Entity, - } -} diff --git a/src/MimeKit/MimeIterator.cs b/src/MimeKit/MimeIterator.cs deleted file mode 100644 index 1503d43..0000000 --- a/src/MimeKit/MimeIterator.cs +++ /dev/null @@ -1,455 +0,0 @@ -// -// MimeIterator.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Collections; -using System.Collections.Generic; - -namespace MimeKit { - /// - /// An iterator for a MIME tree structure. - /// - /// - /// Walks the MIME tree structure of a in depth-first order. - /// - /// - /// - /// - public class MimeIterator : IEnumerator - { - class MimeNode - { - public readonly MimeEntity Entity; - public readonly bool Indexed; - - public MimeNode (MimeEntity entity, bool indexed) - { - Entity = entity; - Indexed = indexed; - } - } - - readonly Stack stack = new Stack (); - readonly List path = new List (); - bool moveFirst = true; - MimeEntity current; - int index = -1; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new for the specified message. - /// - /// - /// - /// - /// The message. - /// - /// is null. - /// - public MimeIterator (MimeMessage message) - { - if (message == null) - throw new ArgumentNullException (nameof (message)); - - Message = message; - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before - /// the is reclaimed by garbage collection. - /// - /// - /// Releases unmanaged resources and performs other cleanup operations before - /// the is reclaimed by garbage collection. - /// - ~MimeIterator () - { - Dispose (false); - } - - /// - /// Gets the top-level message. - /// - /// - /// Gets the top-level message. - /// - /// The message. - public MimeMessage Message { - get; private set; - } - - /// - /// Gets the parent of the current entity. - /// - /// - /// After an iterator is created or after the method is called, - /// the method must be called to advance the iterator to the - /// first entity of the message before reading the value of the Parent property; - /// otherwise, Parent throws a . Parent - /// also throws a if the last call to - /// returned false, which indicates the end of the message. - /// If the current entity is the top-level entity of the message, then the parent - /// will be null; otherwise the parent will be either be a - /// or a . - /// - /// - /// - /// - /// The parent entity. - /// - /// Either has not been called or - /// has moved beyond the end of the message. - /// - public MimeEntity Parent { - get { - if (current == null) - throw new InvalidOperationException (); - - return stack.Count > 0 ? stack.Peek ().Entity : null; - } - } - - /// - /// Gets the current entity. - /// - /// - /// After an iterator is created or after the method is called, - /// the method must be called to advance the iterator to the - /// first entity of the message before reading the value of the Current property; - /// otherwise, Current throws a . Current - /// also throws a if the last call to - /// returned false, which indicates the end of the message. - /// - /// - /// - /// - /// The current entity. - /// - /// Either has not been called or - /// has moved beyond the end of the message. - /// - public MimeEntity Current { - get { - if (current == null) - throw new InvalidOperationException (); - - return current; - } - } - - /// - /// Gets the current entity. - /// - /// - /// After an iterator is created or after the method is called, - /// the method must be called to advance the iterator to the - /// first entity of the message before reading the value of the Current property; - /// otherwise, Current throws a . Current - /// also throws a if the last call to - /// returned false, which indicates the end of the message. - /// - /// The current entity. - /// - /// Either has not been called or - /// has moved beyond the end of the message. - /// - object IEnumerator.Current { - get { return Current; } - } - - /// - /// Gets the path specifier for the current entity. - /// - /// - /// After an iterator is created or after the method is called, - /// the method must be called to advance the iterator to the - /// first entity of the message before reading the value of the PathSpecifier property; - /// otherwise, PathSpecifier throws a . - /// PathSpecifier also throws a if the - /// last call to returned false, which indicates the end of - /// the message. - /// - /// The path specifier. - /// - /// Either has not been called or - /// has moved beyond the end of the message. - /// - public string PathSpecifier { - get { - if (current == null) - throw new InvalidOperationException (); - - var specifier = new StringBuilder (); - - for (int i = 0; i < path.Count; i++) - specifier.AppendFormat ("{0}.", path[i] + 1); - - specifier.AppendFormat ("{0}", index + 1); - - return specifier.ToString (); - } - } - - /// - /// Gets the depth of the current entity. - /// - /// - /// After an iterator is created or after the method is called, - /// the method must be called to advance the iterator to the - /// first entity of the message before reading the value of the Depth property; - /// otherwise, Depth throws a . Depth - /// also throws a if the last call to - /// returned false, which indicates the end of the message. - /// - /// The depth. - /// - /// Either has not been called or - /// has moved beyond the end of the message. - /// - public int Depth { - get { - if (current == null) - throw new InvalidOperationException (); - - return stack.Count; - } - } - - void Push (MimeEntity entity) - { - if (index != -1) - path.Add (index); - - stack.Push (new MimeNode (entity, index != -1)); - } - - bool Pop () - { - if (stack.Count == 0) - return false; - - var node = stack.Pop (); - - if (node.Indexed) { - index = path[path.Count - 1]; - path.RemoveAt (path.Count - 1); - } - - current = node.Entity; - - return true; - } - - /// - /// Advances the iterator to the next depth-first entity of the tree structure. - /// - /// - /// After an iterator is created or after the method is called, - /// an iterator is positioned before the first entity of the message, and the first - /// call to the MoveNext method moves the iterator to the first entity of the message. - /// If MoveNext advances beyond the last entity of the message, MoveNext returns false. - /// When the iterator is at this position, subsequent calls to MoveNext also return - /// false until is called. - /// - /// - /// - /// - /// true if the iterator was successfully advanced to the next entity; otherwise, false. - public bool MoveNext () - { - if (moveFirst) { - current = Message.Body; - moveFirst = false; - - return current != null; - } - - var message_part = current as MessagePart; - var multipart = current as Multipart; - - if (message_part != null) { - current = message_part.Message != null ? message_part.Message.Body : null; - - if (current != null) { - Push (message_part); - index = current is Multipart ? -1 : 0; - return true; - } - } - - if (multipart != null) { - if (multipart.Count > 0) { - Push (current); - current = multipart[0]; - index = 0; - return true; - } - } - - // find the next sibling - while (stack.Count > 0) { - multipart = stack.Peek ().Entity as Multipart; - - if (multipart != null) { - // advance to the next part in the multipart... - if (multipart.Count > ++index) { - current = multipart[index]; - return true; - } - } - - if (!Pop ()) - break; - } - - current = null; - index = -1; - - return false; - } - - static int[] Parse (string pathSpecifier) - { - var path = pathSpecifier.Split ('.'); - var indexes = new int[path.Length]; - int index; - - for (int i = 0; i < path.Length; i++) { - if (!int.TryParse (path[i], out index) || index < 0) - throw new FormatException ("Invalid path specifier format."); - - indexes[i] = index - 1; - } - - return indexes; - } - - /// - /// Advances to the entity specified by the path specifier. - /// - /// - /// Advances the iterator to the entity specified by the path specifier which - /// must be in the same format as returned by . - /// If the iterator has already advanced beyond the entity at the specified - /// path, the iterator will and advance as normal. - /// - /// true if advancing to the specified entity was successful; otherwise, false. - /// The path specifier. - /// - /// is null. - /// - /// - /// is empty. - /// - /// - /// is in an invalid format. - /// - public bool MoveTo (string pathSpecifier) - { - if (pathSpecifier == null) - throw new ArgumentNullException (nameof (pathSpecifier)); - - if (pathSpecifier.Length == 0) - throw new ArgumentException ("The path specifier cannot be empty.", nameof (pathSpecifier)); - - var indexes = Parse (pathSpecifier); - int i; - - // OPTIMIZATION: only reset the iterator if we are jumping to a previous part - for (i = 0; i < Math.Min (indexes.Length, path.Count); i++) { - if (indexes[i] < path[i]) { - Reset (); - break; - } - } - - if (!moveFirst && indexes.Length < path.Count) - Reset (); - - if (moveFirst && !MoveNext ()) - return false; - - do { - if (path.Count + 1 == indexes.Length) { - for (i = 0; i < path.Count; i++) { - if (indexes[i] != path[i]) - break; - } - - if (i == path.Count && indexes[i] == index) - return true; - } - } while (MoveNext ()); - - return false; - } - - /// - /// Resets the iterator to its initial state. - /// - /// - /// Resets the iterator to its initial state. - /// - public void Reset () - { - moveFirst = true; - current = null; - stack.Clear (); - path.Clear (); - index = -1; - } - - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// - /// Releases the unmanaged resources used by the and - /// optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; - /// false to release only the unmanaged resources. - protected virtual void Dispose (bool disposing) - { - } - - /// - /// Releases all resources used by the object. - /// - /// Call when you are finished using the . The - /// method leaves the in an unusable state. After - /// calling , you must release all references to the so - /// the garbage collector can reclaim the memory that the was occupying. - public void Dispose () - { - Dispose (true); - GC.SuppressFinalize (this); - } - } -} diff --git a/src/MimeKit/MimeMessage.cs b/src/MimeKit/MimeMessage.cs deleted file mode 100644 index 35fada6..0000000 --- a/src/MimeKit/MimeMessage.cs +++ /dev/null @@ -1,3185 +0,0 @@ -// -// MimeMessage.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Linq; -using System.Threading; -using System.Globalization; -using System.Threading.Tasks; -using System.Collections.Generic; - -#if ENABLE_SNM -using System.Net.Mail; -#endif - -#if ENABLE_CRYPTO -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Crypto.Parameters; - -using MimeKit.Cryptography; -#endif - -using MimeKit.IO; -using MimeKit.Text; -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A MIME message. - /// - /// - /// A message consists of header fields and, optionally, a body. - /// The body of the message can either be plain text or it can be a - /// tree of MIME entities such as a text/plain MIME part and a collection - /// of file attachments. - /// - public class MimeMessage - { - static readonly string[] StandardAddressHeaders = { - "Resent-From", "Resent-Reply-To", "Resent-To", "Resent-Cc", "Resent-Bcc", - "From", "Reply-To", "To", "Cc", "Bcc" - }; - - readonly Dictionary addresses; - MessageImportance importance = MessageImportance.Normal; - XMessagePriority xpriority = XMessagePriority.Normal; - MessagePriority priority = MessagePriority.Normal; - readonly RfcComplianceMode compliance; - readonly MessageIdList references; - MailboxAddress resentSender; - DateTimeOffset resentDate; - string resentMessageId; - MailboxAddress sender; - DateTimeOffset date; - string messageId; - string inreplyto; - Version version; - - // Note: this .ctor is used only by the MimeParser and MimeMessage.CreateFromMailMessage() - internal MimeMessage (ParserOptions options, IEnumerable
headers, RfcComplianceMode mode) - { - addresses = new Dictionary (MimeUtils.OrdinalIgnoreCase); - Headers = new HeaderList (options); - - compliance = mode; - - // initialize our address lists - foreach (var name in StandardAddressHeaders) { - var list = new InternetAddressList (); - list.Changed += InternetAddressListChanged; - addresses.Add (name, list); - } - - references = new MessageIdList (); - references.Changed += ReferencesChanged; - inreplyto = null; - - Headers.Changed += HeadersChanged; - - // add all of our message headers... - foreach (var header in headers) { - if (header.Field.StartsWith ("Content-", StringComparison.OrdinalIgnoreCase)) - continue; - - Headers.Add (header); - } - } - - internal MimeMessage (ParserOptions options) - { - addresses = new Dictionary (MimeUtils.OrdinalIgnoreCase); - Headers = new HeaderList (options); - - compliance = RfcComplianceMode.Strict; - - // initialize our address lists - foreach (var name in StandardAddressHeaders) { - var list = new InternetAddressList (); - list.Changed += InternetAddressListChanged; - addresses.Add (name, list); - } - - references = new MessageIdList (); - references.Changed += ReferencesChanged; - inreplyto = null; - - Headers.Changed += HeadersChanged; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// An array of initialization parameters: headers and message parts. - /// - /// is null. - /// - /// - /// contains more than one . - /// -or- - /// contains one or more arguments of an unknown type. - /// - public MimeMessage (params object[] args) : this (ParserOptions.Default.Clone ()) - { - if (args == null) - throw new ArgumentNullException (nameof (args)); - - MimeEntity body = null; - - foreach (object obj in args) { - if (obj == null) - continue; - - // Just add the headers and let the events (already setup) keep the - // addresses in sync. - - var header = obj as Header; - if (header != null) { - if (!header.Field.StartsWith ("Content-", StringComparison.OrdinalIgnoreCase)) - Headers.Add (header); - - continue; - } - - var headers = obj as IEnumerable
; - if (headers != null) { - foreach (var h in headers) { - if (!h.Field.StartsWith ("Content-", StringComparison.OrdinalIgnoreCase)) - Headers.Add (h); - } - - continue; - } - - var entity = obj as MimeEntity; - if (entity != null) { - if (body != null) - throw new ArgumentException ("Message body should not be specified more than once."); - - body = entity; - continue; - } - - throw new ArgumentException ("Unknown initialization parameter: " + obj.GetType ()); - } - - if (body != null) - Body = body; - - // Do exactly as in the parameterless constructor but avoid setting a default - // value if an header already provided one. - - if (!Headers.Contains (HeaderId.From)) - Headers[HeaderId.From] = string.Empty; - if (date == default (DateTimeOffset)) - Date = DateTimeOffset.Now; - if (!Headers.Contains (HeaderId.Subject)) - Subject = string.Empty; - if (messageId == null) - MessageId = MimeUtils.GenerateMessageId (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new MIME message, specifying details at creation time. - /// - /// The list of addresses in the From header. - /// The list of addresses in the To header. - /// The subject of the message. - /// The body of the message. - public MimeMessage (IEnumerable from, IEnumerable to, string subject, MimeEntity body) : this () - { - From.AddRange (from); - To.AddRange (to); - Subject = subject; - Body = body; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new MIME message. - /// - public MimeMessage () : this (ParserOptions.Default.Clone ()) - { - Headers[HeaderId.From] = string.Empty; - Date = DateTimeOffset.Now; - Subject = string.Empty; - MessageId = MimeUtils.GenerateMessageId (); - } - - /// - /// Get or set the mbox marker. - /// - /// - /// Set by the when parsing attached message/rfc822 parts - /// so that the message/rfc822 part can be reserialized back to its original form. - /// - /// The mbox marker. - internal byte[] MboxMarker { - get; set; - } - - /// - /// Get the list of headers. - /// - /// - /// Represents the list of headers for a message. Typically, the headers of - /// a message will contain transmission headers such as From and To along - /// with metadata headers such as Subject and Date, but may include just - /// about anything. - /// To access any MIME headers other than - /// , you will need to access the - /// property of the . - /// - /// - /// The list of headers. - public HeaderList Headers { - get; private set; - } - - /// - /// Get or set the value of the Importance header. - /// - /// - /// Gets or sets the value of the Importance header. - /// - /// The importance. - /// - /// is not a valid . - /// - public MessageImportance Importance { - get { return importance; } - set { - if (value == importance) - return; - - switch (value) { - case MessageImportance.Normal: - case MessageImportance.High: - case MessageImportance.Low: - SetHeader ("Importance", value.ToString ().ToLowerInvariant ()); - importance = value; - break; - default: - throw new ArgumentOutOfRangeException (nameof (value)); - } - } - } - - /// - /// Get or set the value of the Priority header. - /// - /// - /// Gets or sets the value of the Priority header. - /// - /// The priority. - /// - /// is not a valid . - /// - public MessagePriority Priority { - get { return priority; } - set { - if (value == priority) - return; - - string rawValue; - - switch (value) { - case MessagePriority.NonUrgent: - rawValue = "non-urgent"; - break; - case MessagePriority.Normal: - rawValue = "normal"; - break; - case MessagePriority.Urgent: - rawValue = "urgent"; - break; - default: - throw new ArgumentOutOfRangeException (nameof (value)); - } - - SetHeader ("Priority", rawValue); - - priority = value; - } - } - - /// - /// Get or set the value of the X-Priority header. - /// - /// - /// Gets or sets the value of the X-Priority header. - /// - /// The priority. - /// - /// is not a valid . - /// - public XMessagePriority XPriority { - get { return xpriority; } - set { - if (value == xpriority) - return; - - string rawValue; - - switch (value) { - case XMessagePriority.Highest: - rawValue = "1 (Highest)"; - break; - case XMessagePriority.High: - rawValue = "2 (High)"; - break; - case XMessagePriority.Normal: - rawValue = "3 (Normal)"; - break; - case XMessagePriority.Low: - rawValue = "4 (Low)"; - break; - case XMessagePriority.Lowest: - rawValue = "5 (Lowest)"; - break; - default: - throw new ArgumentOutOfRangeException (nameof (value)); - } - - SetHeader ("X-Priority", rawValue); - - xpriority = value; - } - } - - /// - /// Get or set the address in the Sender header. - /// - /// - /// The sender may differ from the addresses in if - /// the message was sent by someone on behalf of someone else. - /// - /// The address in the Sender header. - public MailboxAddress Sender { - get { return sender; } - set { - if (value == sender) - return; - - if (value == null) { - RemoveHeader (HeaderId.Sender); - sender = null; - return; - } - - var options = FormatOptions.Default; - var builder = new StringBuilder (" "); - int len = "Sender: ".Length; - - value.Encode (options, builder, true, ref len); - builder.Append (options.NewLine); - - var raw = Encoding.UTF8.GetBytes (builder.ToString ()); - - ReplaceHeader (HeaderId.Sender, "Sender", raw); - - sender = value; - } - } - - /// - /// Get or set the address in the Resent-Sender header. - /// - /// - /// The resent sender may differ from the addresses in if - /// the message was sent by someone on behalf of someone else. - /// - /// The address in the Resent-Sender header. - public MailboxAddress ResentSender { - get { return resentSender; } - set { - if (value == resentSender) - return; - - if (value == null) { - RemoveHeader (HeaderId.ResentSender); - resentSender = null; - return; - } - - var options = FormatOptions.Default; - var builder = new StringBuilder (" "); - int len = "Resent-Sender: ".Length; - - value.Encode (options, builder, true, ref len); - builder.Append (options.NewLine); - - var raw = Encoding.UTF8.GetBytes (builder.ToString ()); - - ReplaceHeader (HeaderId.ResentSender, "Resent-Sender", raw); - - resentSender = value; - } - } - - /// - /// Get the list of addresses in the From header. - /// - /// - /// The "From" header specifies the author(s) of the message. - /// If more than one is added to the - /// list of "From" addresses, the should be set to the - /// single of the personal actually sending - /// the message. - /// - /// The list of addresses in the From header. - public InternetAddressList From { - get { return addresses["From"]; } - } - - /// - /// Get the list of addresses in the Resent-From header. - /// - /// - /// The "Resent-From" header specifies the author(s) of the messagebeing - /// resent. - /// If more than one is added to the - /// list of "Resent-From" addresses, the should - /// be set to the single of the personal actually - /// sending the message. - /// - /// The list of addresses in the Resent-From header. - public InternetAddressList ResentFrom { - get { return addresses["Resent-From"]; } - } - - /// - /// Get the list of addresses in the Reply-To header. - /// - /// - /// When the list of addresses in the Reply-To header is not empty, - /// it contains the address(es) where the author(s) of the message prefer - /// that replies be sent. - /// When the list of addresses in the Reply-To header is empty, - /// replies should be sent to the mailbox(es) specified in the From - /// header. - /// - /// The list of addresses in the Reply-To header. - public InternetAddressList ReplyTo { - get { return addresses["Reply-To"]; } - } - - /// - /// Get the list of addresses in the Resent-Reply-To header. - /// - /// - /// When the list of addresses in the Resent-Reply-To header is not empty, - /// it contains the address(es) where the author(s) of the resent message prefer - /// that replies be sent. - /// When the list of addresses in the Resent-Reply-To header is empty, - /// replies should be sent to the mailbox(es) specified in the Resent-From - /// header. - /// - /// The list of addresses in the Resent-Reply-To header. - public InternetAddressList ResentReplyTo { - get { return addresses["Resent-Reply-To"]; } - } - - /// - /// Get the list of addresses in the To header. - /// - /// - /// The addresses in the To header are the primary recipients of - /// the message. - /// - /// The list of addresses in the To header. - public InternetAddressList To { - get { return addresses["To"]; } - } - - /// - /// Get the list of addresses in the Resent-To header. - /// - /// - /// The addresses in the Resent-To header are the primary recipients of - /// the message. - /// - /// The list of addresses in the Resent-To header. - public InternetAddressList ResentTo { - get { return addresses["Resent-To"]; } - } - - /// - /// Get the list of addresses in the Cc header. - /// - /// - /// The addresses in the Cc header are secondary recipients of the message - /// and are usually not the individuals being directly addressed in the - /// content of the message. - /// - /// The list of addresses in the Cc header. - public InternetAddressList Cc { - get { return addresses["Cc"]; } - } - - /// - /// Get the list of addresses in the Resent-Cc header. - /// - /// - /// The addresses in the Resent-Cc header are secondary recipients of the message - /// and are usually not the individuals being directly addressed in the - /// content of the message. - /// - /// The list of addresses in the Resent-Cc header. - public InternetAddressList ResentCc { - get { return addresses["Resent-Cc"]; } - } - - /// - /// Get the list of addresses in the Bcc header. - /// - /// - /// Recipients in the Blind-Carpbon-Copy list will not be visible to - /// the other recipients of the message. - /// - /// The list of addresses in the Bcc header. - public InternetAddressList Bcc { - get { return addresses["Bcc"]; } - } - - /// - /// Get the list of addresses in the Resent-Bcc header. - /// - /// - /// Recipients in the Resent-Bcc list will not be visible to - /// the other recipients of the message. - /// - /// The list of addresses in the Resent-Bcc header. - public InternetAddressList ResentBcc { - get { return addresses["Resent-Bcc"]; } - } - - /// - /// Get or set the subject of the message. - /// - /// - /// The Subject is typically a short string denoting the topic of the message. - /// Replies will often use "Re: " followed by the Subject of the original message. - /// - /// The subject of the message. - /// - /// is null. - /// - public string Subject { - get { return Headers["Subject"]; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - SetHeader ("Subject", value); - } - } - - /// - /// Get or set the date of the message. - /// - /// - /// If the date is not explicitly set before the message is written to a stream, - /// the date will default to the exact moment when it is written to said stream. - /// - /// The date of the message. - public DateTimeOffset Date { - get { return date; } - set { - if (date == value) - return; - - SetHeader ("Date", DateUtils.FormatDate (value)); - date = value; - } - } - - /// - /// Get or set the Resent-Date of the message. - /// - /// - /// Gets or sets the Resent-Date of the message. - /// - /// The Resent-Date of the message. - public DateTimeOffset ResentDate { - get { return resentDate; } - set { - if (resentDate == value) - return; - - SetHeader ("Resent-Date", DateUtils.FormatDate (value)); - resentDate = value; - } - } - - /// - /// Get the list of references to other messages. - /// - /// - /// The References header contains a chain of Message-Ids back to the - /// original message that started the thread. - /// - /// The references. - public MessageIdList References { - get { return references; } - } - - /// - /// Get or set the Message-Id that this message is replying to. - /// - /// - /// If the message is a reply to another message, it will typically - /// use the In-Reply-To header to specify the Message-Id of the - /// original message being replied to. - /// - /// The message id that this message is in reply to. - /// - /// is improperly formatted. - /// - public string InReplyTo { - get { return inreplyto; } - set { - if (inreplyto == value) - return; - - if (value == null) { - RemoveHeader (HeaderId.InReplyTo); - inreplyto = null; - return; - } - - var buffer = Encoding.UTF8.GetBytes (value); - MailboxAddress mailbox; - int index = 0; - - if (!MailboxAddress.TryParse (Headers.Options, buffer, ref index, buffer.Length, false, out mailbox)) - throw new ArgumentException ("Invalid Message-Id format.", nameof (value)); - - inreplyto = mailbox.Address; - - SetHeader ("In-Reply-To", "<" + inreplyto + ">"); - } - } - - /// - /// Get or set the message identifier. - /// - /// - /// The Message-Id is meant to be a globally unique identifier for - /// a message. - /// can be used - /// to generate this value. - /// - /// The message identifier. - /// - /// is null. - /// - /// - /// is improperly formatted. - /// - public string MessageId { - get { return messageId; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (messageId == value) - return; - - var buffer = Encoding.UTF8.GetBytes (value); - MailboxAddress mailbox; - int index = 0; - - if (!MailboxAddress.TryParse (Headers.Options, buffer, ref index, buffer.Length, false, out mailbox)) - throw new ArgumentException ("Invalid Message-Id format.", nameof (value)); - - messageId = mailbox.Address; - - SetHeader ("Message-Id", "<" + messageId + ">"); - } - } - - /// - /// Get or set the Resent-Message-Id header. - /// - /// - /// The Resent-Message-Id is meant to be a globally unique identifier for - /// a message. - /// can be used - /// to generate this value. - /// - /// The Resent-Message-Id. - /// - /// is null. - /// - /// - /// is improperly formatted. - /// - public string ResentMessageId { - get { return resentMessageId; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (resentMessageId == value) - return; - - var buffer = Encoding.UTF8.GetBytes (value); - MailboxAddress mailbox; - int index = 0; - - if (!MailboxAddress.TryParse (Headers.Options, buffer, ref index, buffer.Length, false, out mailbox)) - throw new ArgumentException ("Invalid Resent-Message-Id format.", nameof (value)); - - resentMessageId = mailbox.Address; - - SetHeader ("Resent-Message-Id", "<" + resentMessageId + ">"); - } - } - - /// - /// Get or set the MIME-Version. - /// - /// - /// The MIME-Version header specifies the version of the MIME specification - /// that the message was created for. - /// - /// The MIME version. - /// - /// is null. - /// - public Version MimeVersion { - get { return version; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (version != null && version.CompareTo (value) == 0) - return; - - SetHeader ("MIME-Version", value.ToString ()); - version = value; - } - } - - /// - /// Get or set the body of the message. - /// - /// - /// The body of the message can either be plain text or it can be a - /// tree of MIME entities such as a text/plain MIME part and a collection - /// of file attachments. - /// For a convenient way of constructing message bodies, see the - /// class. - /// - /// The body of the message. - public MimeEntity Body { - get; set; - } - - static bool TryGetMultipartBody (Multipart multipart, TextFormat format, out string body) - { - var alternative = multipart as MultipartAlternative; - - if (alternative != null) { - body = alternative.GetTextBody (format); - return body != null; - } - - var related = multipart as MultipartRelated; - Multipart multi; - TextPart text; - - if (related == null) { - // Note: This is probably a multipart/mixed... and if not, we can still treat it like it is. - for (int i = 0; i < multipart.Count; i++) { - multi = multipart[i] as Multipart; - - // descend into nested multiparts, if there are any... - if (multi != null) { - if (TryGetMultipartBody (multi, format, out body)) - return true; - - // The text body should never come after a multipart. - break; - } - - text = multipart[i] as TextPart; - - // Look for the first non-attachment text part (realistically, the body text will - // preceed any attachments, but I'm not sure we can rely on that assumption). - if (text != null && !text.IsAttachment) { - if (text.IsFormat (format)) { - body = MultipartAlternative.GetText (text); - return true; - } - - // Note: the first text/* part in a multipart/mixed is the text body. - // If it's not in the format we're looking for, then it doesn't exist. - break; - } - } - } else { - // Note: If the multipart/related root document is HTML, then this is the droid we are looking for. - var root = related.Root; - - text = root as TextPart; - - if (text != null) { - body = text.IsFormat (format) ? text.Text : null; - return body != null; - } - - // maybe the root is another multipart (like multipart/alternative)? - multi = root as Multipart; - - if (multi != null) - return TryGetMultipartBody (multi, format, out body); - } - - body = null; - - return false; - } - - /// - /// Get the text body of the message if it exists. - /// - /// - /// Gets the text content of the first text/plain body part that is found (in depth-first - /// search order) which is not an attachment. - /// - /// The text body if it exists; otherwise, null. - public string TextBody { - get { return GetTextBody (TextFormat.Plain); } - } - - /// - /// Get the html body of the message if it exists. - /// - /// - /// Gets the HTML-formatted body of the message if it exists. - /// - /// The html body if it exists; otherwise, null. - public string HtmlBody { - get { return GetTextBody (TextFormat.Html); } - } - - /// - /// Get the text body in the specified format. - /// - /// - /// Gets the text body in the specified format, if it exists. - /// - /// The text body in the desired format if it exists; otherwise, null. - /// The desired text format. - public string GetTextBody (TextFormat format) - { - var multipart = Body as Multipart; - - if (multipart != null) { - string text; - - if (TryGetMultipartBody (multipart, format, out text)) - return text; - } else { - var body = Body as TextPart; - - if (body != null && body.IsFormat (format) && !body.IsAttachment) - return body.Text; - } - - return null; - } - - static IEnumerable EnumerateMimeParts (MimeEntity entity) - { - if (entity == null) - yield break; - - var multipart = entity as Multipart; - - if (multipart != null) { - foreach (var subpart in multipart) { - foreach (var part in EnumerateMimeParts (subpart)) - yield return part; - } - - yield break; - } - - yield return entity; - } - - /// - /// Get the body parts of the message. - /// - /// - /// Traverses over the MIME tree, enumerating all of the objects, - /// but does not traverse into the bodies of attached messages. - /// - /// - /// - /// - /// The body parts. - public IEnumerable BodyParts { - get { return EnumerateMimeParts (Body); } - } - - /// - /// Get the attachments. - /// - /// - /// Traverses over the MIME tree, enumerating all of the objects that - /// have a Content-Disposition header set to "attachment". - /// - /// - /// - /// - /// The attachments. - public IEnumerable Attachments { - get { return EnumerateMimeParts (Body).Where (x => x.IsAttachment); } - } - - /// - /// Returns a that represents the for debugging purposes. - /// - /// - /// Returns a that represents the for debugging purposes. - /// In general, the string returned from this method SHOULD NOT be used for serializing - /// the message to disk. It is recommended that you use instead. - /// If this method is used for serializing the message to disk, the iso-8859-1 text encoding should be used for - /// conversion. - /// - /// A that represents the for debugging purposes. - public override string ToString () - { - using (var memory = new MemoryStream ()) { - WriteTo (FormatOptions.Default, memory); - -#if !NETSTANDARD1_3 && !NETSTANDARD1_6 - var buffer = memory.GetBuffer (); -#else - var buffer = memory.ToArray (); -#endif - int count = (int) memory.Length; - - return CharsetUtils.Latin1.GetString (buffer, 0, count); - } - } - - /// - /// Dispatches to the specific visit method for this MIME message. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public virtual void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMimeMessage (this); - } - - /// - /// Prepare the message for transport using the specified encoding constraints. - /// - /// - /// Prepares the message for transport using the specified encoding constraints. - /// - /// The encoding constraint. - /// The maximum allowable length for a line (not counting the CRLF). Must be between 60 and 998 (inclusive). - /// - /// is not between 60 and 998 (inclusive). - /// -or- - /// is not a valid value. - /// - public virtual void Prepare (EncodingConstraint constraint, int maxLineLength = 78) - { - if (maxLineLength < FormatOptions.MinimumLineLength || maxLineLength > FormatOptions.MaximumLineLength) - throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - - if (Body != null) { - if (MimeVersion == null && Body.Headers.Count > 0) - MimeVersion = new Version (1, 0); - - Body.Prepare (constraint, maxLineLength); - } - } - - /// - /// Write the message to the specified output stream. - /// - /// - /// Writes the message to the output stream using the provided formatting options. - /// - /// The formatting options. - /// The output stream. - /// true if only the headers should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (FormatOptions options, Stream stream, bool headersOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (compliance == RfcComplianceMode.Strict && Body != null && Body.Headers.Count > 0 && !Headers.Contains (HeaderId.MimeVersion)) - MimeVersion = new Version (1, 0); - - if (Body != null) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - foreach (var header in MergeHeaders ()) { - if (options.HiddenHeaders.Contains (header.Id)) - continue; - - filtered.Write (header.RawField, 0, header.RawField.Length, cancellationToken); - - if (!header.IsInvalid) { - var rawValue = header.GetRawValue (options); - - filtered.Write (Header.Colon, 0, Header.Colon.Length, cancellationToken); - filtered.Write (rawValue, 0, rawValue.Length, cancellationToken); - } - } - - filtered.Flush (cancellationToken); - } - - var cancellable = stream as ICancellableStream; - - if (cancellable != null) { - cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - - if (!headersOnly) { - try { - Body.EnsureNewLine = compliance == RfcComplianceMode.Strict || options.EnsureNewLine; - Body.WriteTo (options, stream, true, cancellationToken); - } finally { - Body.EnsureNewLine = false; - } - } - } else { - Headers.WriteTo (options, stream, cancellationToken); - } - } - - /// - /// Asynchronously write the message to the specified output stream. - /// - /// - /// Asynchronously writes the message to the output stream using the provided formatting options. - /// - /// An awaitable task. - /// The formatting options. - /// The output stream. - /// true if only the headers should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public async Task WriteToAsync (FormatOptions options, Stream stream, bool headersOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - if (compliance == RfcComplianceMode.Strict && Body != null && Body.Headers.Count > 0 && !Headers.Contains (HeaderId.MimeVersion)) - MimeVersion = new Version (1, 0); - - if (Body != null) { - using (var filtered = new FilteredStream (stream)) { - filtered.Add (options.CreateNewLineFilter ()); - - foreach (var header in MergeHeaders ()) { - if (options.HiddenHeaders.Contains (header.Id)) - continue; - - await filtered.WriteAsync (header.RawField, 0, header.RawField.Length, cancellationToken).ConfigureAwait (false); - - if (!header.IsInvalid) { - var rawValue = header.GetRawValue (options); - - await filtered.WriteAsync (Header.Colon, 0, Header.Colon.Length, cancellationToken).ConfigureAwait (false); - await filtered.WriteAsync (rawValue, 0, rawValue.Length, cancellationToken).ConfigureAwait (false); - } - } - - await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); - } - - await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); - - if (!headersOnly) { - try { - Body.EnsureNewLine = compliance == RfcComplianceMode.Strict || options.EnsureNewLine; - await Body.WriteToAsync (options, stream, true, cancellationToken).ConfigureAwait (false); - } finally { - Body.EnsureNewLine = false; - } - } - } else { - await Headers.WriteToAsync (options, stream, cancellationToken).ConfigureAwait (false); - } - } - - /// - /// Write the message to the specified output stream. - /// - /// - /// Writes the message to the output stream using the provided formatting options. - /// - /// The formatting options. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (options, stream, false, cancellationToken); - } - - /// - /// Asynchronously write the message to the specified output stream. - /// - /// - /// Asynchronously writes the message to the output stream using the provided formatting options. - /// - /// An awaitable task. - /// The formatting options. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (options, stream, false, cancellationToken); - } - - /// - /// Write the message to the specified output stream. - /// - /// - /// Writes the message to the output stream using the default formatting options. - /// - /// The output stream. - /// true if only the headers should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (Stream stream, bool headersOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (FormatOptions.Default, stream, headersOnly, cancellationToken); - } - - /// - /// Asynchronously write the message to the specified output stream. - /// - /// - /// Asynchronously writes the message to the output stream using the default formatting options. - /// - /// An awaitable task. - /// The output stream. - /// true if only the headers should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (Stream stream, bool headersOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (FormatOptions.Default, stream, headersOnly, cancellationToken); - } - - /// - /// Write the message to the specified output stream. - /// - /// - /// Writes the message to the output stream using the default formatting options. - /// - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (FormatOptions.Default, stream, false, cancellationToken); - } - - /// - /// Asynchronously write the message to the specified output stream. - /// - /// - /// Asynchronously writes the message to the output stream using the default formatting options. - /// - /// An awaitable task. - /// The output stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (FormatOptions.Default, stream, false, cancellationToken); - } - - /// - /// Write the message to the specified file. - /// - /// - /// Writes the message to the specified file using the provided formatting options. - /// - /// The formatting options. - /// The file. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (FormatOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) { - WriteTo (options, stream, cancellationToken); - stream.Flush (); - } - } - - /// - /// Asynchronously write the message to the specified file. - /// - /// - /// Asynchronously writes the message to the specified file using the provided formatting options. - /// - /// An awaitable task. - /// The formatting options. - /// The file. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public async Task WriteToAsync (FormatOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.Open (fileName, FileMode.Create, FileAccess.Write)) { - await WriteToAsync (options, stream, cancellationToken).ConfigureAwait (false); - await stream.FlushAsync (cancellationToken).ConfigureAwait (false); - } - } - - /// - /// Write the message to the specified file. - /// - /// - /// Writes the message to the specified file using the default formatting options. - /// - /// The file. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public void WriteTo (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - WriteTo (FormatOptions.Default, fileName, cancellationToken); - } - - /// - /// Asynchronously write the message to the specified file. - /// - /// - /// Asynchronously writes the message to the specified file using the default formatting options. - /// - /// An awaitable task. - /// The file. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to write to the specified file. - /// - /// - /// An I/O error occurred. - /// - public Task WriteToAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - return WriteToAsync (FormatOptions.Default, fileName, cancellationToken); - } - - MailboxAddress GetMessageSigner () - { - if (ResentSender != null) - return ResentSender; - - if (ResentFrom.Count > 0) - return ResentFrom.Mailboxes.FirstOrDefault (); - - if (Sender != null) - return Sender; - - if (From.Count > 0) - return From.Mailboxes.FirstOrDefault (); - - return null; - } - - IList GetMessageRecipients (bool includeSenders) - { - var recipients = new HashSet (); - - if (ResentSender != null || ResentFrom.Count > 0) { - if (includeSenders) { - if (ResentSender != null) - recipients.Add (ResentSender); - - if (ResentFrom.Count > 0) { - foreach (var mailbox in ResentFrom.Mailboxes) - recipients.Add (mailbox); - } - } - - foreach (var mailbox in ResentTo.Mailboxes) - recipients.Add (mailbox); - foreach (var mailbox in ResentCc.Mailboxes) - recipients.Add (mailbox); - foreach (var mailbox in ResentBcc.Mailboxes) - recipients.Add (mailbox); - } else { - if (includeSenders) { - if (Sender != null) - recipients.Add (Sender); - - if (From.Count > 0) { - foreach (var mailbox in From.Mailboxes) - recipients.Add (mailbox); - } - } - - foreach (var mailbox in To.Mailboxes) - recipients.Add (mailbox); - foreach (var mailbox in Cc.Mailboxes) - recipients.Add (mailbox); - foreach (var mailbox in Bcc.Mailboxes) - recipients.Add (mailbox); - } - - return recipients.ToList (); - } - -#if ENABLE_CRYPTO - internal byte[] HashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) - { - using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) { - using (var filtered = new FilteredStream (stream)) { - DkimBodyFilter dkim; - - if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed) - dkim = new DkimRelaxedBodyFilter (); - else - dkim = new DkimSimpleBodyFilter (); - - filtered.Add (options.CreateNewLineFilter ()); - filtered.Add (dkim); - - if (Body != null) { - try { - Body.EnsureNewLine = compliance == RfcComplianceMode.Strict || options.EnsureNewLine; - Body.WriteTo (options, filtered, true, CancellationToken.None); - } finally { - Body.EnsureNewLine = false; - } - } - - filtered.Flush (); - - if (!dkim.LastWasNewLine) - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - - return stream.GenerateHash (); - } - } - - /// - /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// - /// - /// The formatting options. - /// The DKIM signer. - /// The list of header fields to sign. - /// The header canonicalization algorithm. - /// The body canonicalization algorithm. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - [Obsolete ("Use DkimSigner.Sign() instead.")] - public void Sign (FormatOptions options, DkimSigner signer, IList headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - signer.HeaderCanonicalizationAlgorithm = headerCanonicalizationAlgorithm; - signer.BodyCanonicalizationAlgorithm = bodyCanonicalizationAlgorithm; - - signer.Sign (options, this, headers); - } - - /// - /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// - /// - /// The DKIM signer. - /// The headers to sign. - /// The header canonicalization algorithm. - /// The body canonicalization algorithm. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - [Obsolete ("Use DkimSigner.Sign() instead.")] - public void Sign (DkimSigner signer, IList headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) - { - Sign (FormatOptions.Default, signer, headers, headerCanonicalizationAlgorithm, bodyCanonicalizationAlgorithm); - } - - /// - /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// - /// - /// The formatting options. - /// The DKIM signer. - /// The list of header fields to sign. - /// The header canonicalization algorithm. - /// The body canonicalization algorithm. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - [Obsolete ("Use DkimSigner.Sign() instead.")] - public void Sign (FormatOptions options, DkimSigner signer, IList headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (signer == null) - throw new ArgumentNullException (nameof (signer)); - - signer.HeaderCanonicalizationAlgorithm = headerCanonicalizationAlgorithm; - signer.BodyCanonicalizationAlgorithm = bodyCanonicalizationAlgorithm; - - signer.Sign (options, this, headers); - } - - /// - /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. - /// - /// - /// - /// - /// The DKIM signer. - /// The headers to sign. - /// The header canonicalization algorithm. - /// The body canonicalization algorithm. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// does not contain the 'From' header. - /// -or- - /// contains one or more of the following headers: Return-Path, - /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature. - /// - [Obsolete ("Use DkimSigner.Sign() instead.")] - public void Sign (DkimSigner signer, IList headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) - { - Sign (FormatOptions.Default, signer, headers, headerCanonicalizationAlgorithm, bodyCanonicalizationAlgorithm); - } - - Task DkimVerifyAsync (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, bool doAsync, CancellationToken cancellationToken) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (dkimSignature == null) - throw new ArgumentNullException (nameof (dkimSignature)); - - if (dkimSignature.Id != HeaderId.DkimSignature) - throw new ArgumentException ("The signature parameter MUST be a DKIM-Signature header.", nameof (dkimSignature)); - - var verifier = new DkimVerifier (publicKeyLocator); - - if (doAsync) - return verifier.VerifyAsync (options, this, dkimSignature, cancellationToken); - - return Task.FromResult (verifier.Verify (options, this, dkimSignature, cancellationToken)); - } - - /// - /// Verify the specified DKIM-Signature header. - /// - /// - /// Verifies the specified DKIM-Signature header. - /// - /// - /// - /// - /// true if the DKIM-Signature is valid; otherwise, false. - /// The formatting options. - /// The DKIM-Signature header. - /// The public key locator service. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a DKIM-Signature header. - /// - /// - /// The DKIM-Signature header value is malformed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - [Obsolete ("Use the DkimVerifier class instead.")] - public bool Verify (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) - { - return DkimVerifyAsync (options, dkimSignature, publicKeyLocator, false, cancellationToken).GetAwaiter ().GetResult (); - } - - /// - /// Asynchronously verify the specified DKIM-Signature header. - /// - /// - /// Verifies the specified DKIM-Signature header. - /// - /// - /// - /// - /// true if the DKIM-Signature is valid; otherwise, false. - /// The formatting options. - /// The DKIM-Signature header. - /// The public key locator service. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a DKIM-Signature header. - /// - /// - /// The DKIM-Signature header value is malformed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - [Obsolete ("Use the DkimVerifier class instead.")] - public Task VerifyAsync (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) - { - return DkimVerifyAsync (options, dkimSignature, publicKeyLocator, true, cancellationToken); - } - - /// - /// Verify the specified DKIM-Signature header. - /// - /// - /// Verifies the specified DKIM-Signature header. - /// - /// - /// - /// - /// true if the DKIM-Signature is valid; otherwise, false. - /// The DKIM-Signature header. - /// The public key locator service. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a DKIM-Signature header. - /// - /// - /// The DKIM-Signature header value is malformed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - [Obsolete ("Use the DkimVerifier class instead.")] - public bool Verify (Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) - { - return Verify (FormatOptions.Default, dkimSignature, publicKeyLocator, cancellationToken); - } - - /// - /// Asynchronously verify the specified DKIM-Signature header. - /// - /// - /// Verifies the specified DKIM-Signature header. - /// - /// - /// - /// - /// true if the DKIM-Signature is valid; otherwise, false. - /// The DKIM-Signature header. - /// The public key locator service. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a DKIM-Signature header. - /// - /// - /// The DKIM-Signature header value is malformed. - /// - /// - /// The operation was canceled via the cancellation token. - /// - [Obsolete ("Use the DkimVerifier class instead.")] - public Task VerifyAsync (Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) - { - return VerifyAsync (FormatOptions.Default, dkimSignature, publicKeyLocator, cancellationToken); - } - - /// - /// Sign the message using the specified cryptography context and digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// - /// The cryptography context. - /// The digest algorithm. - /// - /// is null. - /// - /// - /// The has not been set. - /// -or- - /// A sender has not been specified. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// - /// - /// A signing certificate could not be found for the sender. - /// - /// - /// The private key could not be found for the sender. - /// - public void Sign (CryptographyContext ctx, DigestAlgorithm digestAlgo) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body == null) - throw new InvalidOperationException ("No message body has been set."); - - var signer = GetMessageSigner (); - if (signer == null) - throw new InvalidOperationException ("The sender has not been set."); - - Body = MultipartSigned.Create (ctx, signer, digestAlgo, Body); - } - - /// - /// Sign the message using the specified cryptography context and the SHA-1 digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// - /// The cryptography context. - /// - /// is null. - /// - /// - /// The has not been set. - /// -or- - /// A sender has not been specified. - /// - /// - /// A signing certificate could not be found for the sender. - /// - /// - /// The private key could not be found for the sender. - /// - public void Sign (CryptographyContext ctx) - { - Sign (ctx, DigestAlgorithm.Sha1); - } - - /// - /// Encrypt the message to the sender and all of the recipients - /// using the specified cryptography context. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// The cryptography context. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The has not been set. - /// -or- - /// No recipients have been specified. - /// - /// - /// A certificate could not be found for one or more of the recipients. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public void Encrypt (CryptographyContext ctx) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body == null) - throw new InvalidOperationException ("No message body has been set."); - - var recipients = GetMessageRecipients (true); - if (recipients.Count == 0) - throw new InvalidOperationException ("No recipients have been set."); - - if (ctx is SecureMimeContext) { - Body = ApplicationPkcs7Mime.Encrypt ((SecureMimeContext) ctx, recipients, Body); - } else if (ctx is OpenPgpContext) { - Body = MultipartEncrypted.Encrypt ((OpenPgpContext) ctx, recipients, Body); - } else { - throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); - } - } - - /// - /// Sign and encrypt the message to the sender and all of the recipients using - /// the specified cryptography context and the specified digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the - /// message will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// The cryptography context. - /// The digest algorithm. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The was out of range. - /// - /// - /// The has not been set. - /// -or- - /// No sender has been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The is not supported. - /// - /// - /// A certificate could not be found for the signer or one or more of the recipients. - /// - /// - /// The private key could not be found for the sender. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public void SignAndEncrypt (CryptographyContext ctx, DigestAlgorithm digestAlgo) - { - if (ctx == null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body == null) - throw new InvalidOperationException ("No message body has been set."); - - var signer = GetMessageSigner (); - if (signer == null) - throw new InvalidOperationException ("The sender has not been set."); - - var recipients = GetMessageRecipients (true); - - if (ctx is SecureMimeContext) { - Body = ApplicationPkcs7Mime.SignAndEncrypt ((SecureMimeContext) ctx, signer, digestAlgo, recipients, Body); - } else if (ctx is OpenPgpContext) { - Body = MultipartEncrypted.SignAndEncrypt ((OpenPgpContext) ctx, signer, digestAlgo, recipients, Body); - } else { - throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); - } - } - - /// - /// Sign and encrypt the message to the sender and all of the recipients using - /// the specified cryptography context and the SHA-1 digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the - /// message will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// The cryptography context. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The has not been set. - /// -or- - /// No sender has been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// A certificate could not be found for the signer or one or more of the recipients. - /// - /// - /// The private key could not be found for the sender. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public void SignAndEncrypt (CryptographyContext ctx) - { - SignAndEncrypt (ctx, DigestAlgorithm.Sha1); - } -#endif // ENABLE_CRYPTO - - IEnumerable
MergeHeaders () - { - int mesgIndex = 0, bodyIndex = 0; - - // write all of the prepended message headers first - while (mesgIndex < Headers.Count) { - var mesgHeader = Headers[mesgIndex]; - if (mesgHeader.Offset.HasValue) - break; - - yield return mesgHeader; - mesgIndex++; - } - - // now merge the message and body headers as they appeared in the raw message - while (mesgIndex < Headers.Count && bodyIndex < Body.Headers.Count) { - var bodyHeader = Body.Headers[bodyIndex]; - if (!bodyHeader.Offset.HasValue) - break; - - var mesgHeader = Headers[mesgIndex]; - - if (mesgHeader.Offset.HasValue && mesgHeader.Offset < bodyHeader.Offset) { - yield return mesgHeader; - - mesgIndex++; - } else { - yield return bodyHeader; - - bodyIndex++; - } - } - - while (mesgIndex < Headers.Count) - yield return Headers[mesgIndex++]; - - while (bodyIndex < Body.Headers.Count) - yield return Body.Headers[bodyIndex++]; - } - - void RemoveHeader (HeaderId id) - { - Headers.Changed -= HeadersChanged; - - try { - Headers.RemoveAll (id); - } finally { - Headers.Changed += HeadersChanged; - } - } - - void ReplaceHeader (HeaderId id, string name, byte[] raw) - { - Headers.Changed -= HeadersChanged; - - try { - Headers.Replace (new Header (Headers.Options, id, name, raw)); - } finally { - Headers.Changed += HeadersChanged; - } - } - - void SetHeader (string name, string value) - { - Headers.Changed -= HeadersChanged; - - try { - Headers[name] = value; - } finally { - Headers.Changed += HeadersChanged; - } - } - - void SerializeAddressList (string field, InternetAddressList list) - { - if (list.Count == 0) { - RemoveHeader (field.ToHeaderId ()); - return; - } - - var builder = new StringBuilder (" "); - var options = FormatOptions.Default; - int lineLength = field.Length + 2; - - list.Encode (options, builder, true, ref lineLength); - builder.Append (options.NewLine); - - var raw = Encoding.UTF8.GetBytes (builder.ToString ()); - - ReplaceHeader (field.ToHeaderId (), field, raw); - } - - void InternetAddressListChanged (object addrlist, EventArgs e) - { - var list = (InternetAddressList) addrlist; - - foreach (var name in StandardAddressHeaders) { - if (addresses[name] == list) { - SerializeAddressList (name, list); - break; - } - } - } - - void ReferencesChanged (object o, EventArgs e) - { - if (references.Count > 0) { - int lineLength = "References".Length + 1; - var options = FormatOptions.Default; - var builder = new StringBuilder (); - - for (int i = 0; i < references.Count; i++) { - if (i > 0 && lineLength + references[i].Length + 2 >= options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - } else { - builder.Append (' '); - lineLength++; - } - - lineLength += references[i].Length; - builder.Append ("<" + references[i] + ">"); - } - - builder.Append (options.NewLine); - - var raw = Encoding.UTF8.GetBytes (builder.ToString ()); - - ReplaceHeader (HeaderId.References, "References", raw); - } else { - RemoveHeader (HeaderId.References); - } - } - - void AddAddresses (Header header, InternetAddressList list) - { - int length = header.RawValue.Length; - List parsed; - int index = 0; - - // parse the addresses in the new header and add them to our address list - if (!InternetAddressList.TryParse (Headers.Options, header.RawValue, ref index, length, false, 0, false, out parsed)) - return; - - list.Changed -= InternetAddressListChanged; - list.AddRange (parsed); - list.Changed += InternetAddressListChanged; - } - - void ReloadAddressList (HeaderId id, InternetAddressList list) - { - // clear the address list and reload - list.Changed -= InternetAddressListChanged; - list.Clear (); - - foreach (var header in Headers) { - if (header.Id != id) - continue; - - int length = header.RawValue.Length; - List parsed; - int index = 0; - - if (!InternetAddressList.TryParse (Headers.Options, header.RawValue, ref index, length, false, 0, false, out parsed)) - continue; - - list.AddRange (parsed); - } - - list.Changed += InternetAddressListChanged; - } - - void ReloadHeader (HeaderId id) - { - if (id == HeaderId.Unknown) - return; - - switch (id) { - case HeaderId.ResentMessageId: - resentMessageId = null; - break; - case HeaderId.ResentSender: - resentSender = null; - break; - case HeaderId.ResentDate: - resentDate = DateTimeOffset.MinValue; - break; - case HeaderId.References: - references.Changed -= ReferencesChanged; - references.Clear (); - references.Changed += ReferencesChanged; - break; - case HeaderId.InReplyTo: - inreplyto = null; - break; - case HeaderId.MessageId: - messageId = null; - break; - case HeaderId.Sender: - sender = null; - break; - case HeaderId.Importance: - importance = MessageImportance.Normal; - break; - case HeaderId.XPriority: - xpriority = XMessagePriority.Normal; - break; - case HeaderId.Priority: - priority = MessagePriority.Normal; - break; - case HeaderId.Date: - date = DateTimeOffset.MinValue; - break; - } - - foreach (var header in Headers) { - if (header.Id != id) - continue; - - var rawValue = header.RawValue; - int number, index = 0; - - switch (id) { - case HeaderId.MimeVersion: - MimeUtils.TryParse (rawValue, 0, rawValue.Length, out version); - break; - case HeaderId.References: - references.Changed -= ReferencesChanged; - foreach (var msgid in MimeUtils.EnumerateReferences (rawValue, 0, rawValue.Length)) - references.Add (msgid); - references.Changed += ReferencesChanged; - break; - case HeaderId.InReplyTo: - inreplyto = MimeUtils.EnumerateReferences (rawValue, 0, rawValue.Length).FirstOrDefault (); - break; - case HeaderId.ResentMessageId: - resentMessageId = MimeUtils.ParseMessageId (rawValue, 0, rawValue.Length); - break; - case HeaderId.MessageId: - messageId = MimeUtils.ParseMessageId (rawValue, 0, rawValue.Length); - break; - case HeaderId.ResentSender: - MailboxAddress.TryParse (Headers.Options, rawValue, ref index, rawValue.Length, false, out resentSender); - break; - case HeaderId.Sender: - MailboxAddress.TryParse (Headers.Options, rawValue, ref index, rawValue.Length, false, out sender); - break; - case HeaderId.ResentDate: - DateUtils.TryParse (rawValue, 0, rawValue.Length, out resentDate); - break; - case HeaderId.Importance: - switch (header.Value.ToLowerInvariant ().Trim ()) { - case "high": importance = MessageImportance.High; break; - case "low": importance = MessageImportance.Low; break; - default: importance = MessageImportance.Normal; break; - } - break; - case HeaderId.Priority: - switch (header.Value.ToLowerInvariant ().Trim ()) { - case "non-urgent": priority = MessagePriority.NonUrgent; break; - case "urgent": priority = MessagePriority.Urgent; break; - default: priority = MessagePriority.Normal; break; - } - break; - case HeaderId.XPriority: - ParseUtils.SkipWhiteSpace (rawValue, ref index, rawValue.Length); - - if (ParseUtils.TryParseInt32 (rawValue, ref index, rawValue.Length, out number)) { - xpriority = (XMessagePriority) Math.Min (Math.Max (number, 1), 5); - } else { - xpriority = XMessagePriority.Normal; - } - break; - case HeaderId.Date: - DateUtils.TryParse (rawValue, 0, rawValue.Length, out date); - break; - } - } - } - - void HeadersChanged (object o, HeaderListChangedEventArgs e) - { - InternetAddressList list; - byte[] rawValue; - int index = 0; - int number; - - switch (e.Action) { - case HeaderListChangedAction.Added: - if (addresses.TryGetValue (e.Header.Field, out list)) { - AddAddresses (e.Header, list); - break; - } - - rawValue = e.Header.RawValue; - - switch (e.Header.Id) { - case HeaderId.MimeVersion: - MimeUtils.TryParse (rawValue, 0, rawValue.Length, out version); - break; - case HeaderId.References: - references.Changed -= ReferencesChanged; - foreach (var msgid in MimeUtils.EnumerateReferences (rawValue, 0, rawValue.Length)) - references.Add (msgid); - references.Changed += ReferencesChanged; - break; - case HeaderId.InReplyTo: - inreplyto = MimeUtils.EnumerateReferences (rawValue, 0, rawValue.Length).FirstOrDefault (); - break; - case HeaderId.ResentMessageId: - resentMessageId = MimeUtils.ParseMessageId (rawValue, 0, rawValue.Length); - break; - case HeaderId.MessageId: - messageId = MimeUtils.ParseMessageId (rawValue, 0, rawValue.Length); - break; - case HeaderId.ResentSender: - MailboxAddress.TryParse (Headers.Options, rawValue, ref index, rawValue.Length, false, out resentSender); - break; - case HeaderId.Sender: - MailboxAddress.TryParse (Headers.Options, rawValue, ref index, rawValue.Length, false, out sender); - break; - case HeaderId.ResentDate: - DateUtils.TryParse (rawValue, 0, rawValue.Length, out resentDate); - break; - case HeaderId.Importance: - switch (e.Header.Value.ToLowerInvariant ().Trim ()) { - case "high": importance = MessageImportance.High; break; - case "low": importance = MessageImportance.Low; break; - default: importance = MessageImportance.Normal; break; - } - break; - case HeaderId.Priority: - switch (e.Header.Value.ToLowerInvariant ().Trim ()) { - case "non-urgent": priority = MessagePriority.NonUrgent; break; - case "urgent": priority = MessagePriority.Urgent; break; - default: priority = MessagePriority.Normal; break; - } - break; - case HeaderId.XPriority: - ParseUtils.SkipWhiteSpace (rawValue, ref index, rawValue.Length); - - if (ParseUtils.TryParseInt32 (rawValue, ref index, rawValue.Length, out number)) { - xpriority = (XMessagePriority) Math.Min (Math.Max (number, 1), 5); - } else { - xpriority = XMessagePriority.Normal; - } - break; - case HeaderId.Date: - DateUtils.TryParse (rawValue, 0, rawValue.Length, out date); - break; - } - break; - case HeaderListChangedAction.Changed: - case HeaderListChangedAction.Removed: - if (addresses.TryGetValue (e.Header.Field, out list)) { - ReloadAddressList (e.Header.Id, list); - break; - } - - ReloadHeader (e.Header.Id); - break; - case HeaderListChangedAction.Cleared: - foreach (var kvp in addresses) { - kvp.Value.Changed -= InternetAddressListChanged; - kvp.Value.Clear (); - kvp.Value.Changed += InternetAddressListChanged; - } - - references.Changed -= ReferencesChanged; - references.Clear (); - references.Changed += ReferencesChanged; - - resentDate = date = DateTimeOffset.MinValue; - importance = MessageImportance.Normal; - xpriority = XMessagePriority.Normal; - priority = MessagePriority.Normal; - resentMessageId = null; - resentSender = null; - inreplyto = null; - messageId = null; - version = null; - sender = null; - break; - default: - throw new ArgumentOutOfRangeException (); - } - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save mmeory usage, but also improve - /// performance. - /// - /// The parsed message. - /// The parser options. - /// The stream. - /// true if the stream is persistent; otherwise false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeMessage Load (ParserOptions options, Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new MimeParser (options, stream, MimeFormat.Entity, persistent); - - return parser.ParseMessage (cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save mmeory usage, but also improve - /// performance. - /// - /// The parsed message. - /// The parser options. - /// The stream. - /// true if the stream is persistent; otherwise false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (ParserOptions options, Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - var parser = new MimeParser (options, stream, MimeFormat.Entity, persistent); - - return parser.ParseMessageAsync (cancellationToken); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// - /// The parsed message. - /// The parser options. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeMessage Load (ParserOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (options, stream, false, cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// specified . - /// - /// The parsed message. - /// The parser options. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (ParserOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (options, stream, false, cancellationToken); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save mmeory usage, but also improve - /// performance. - /// - /// The parsed message. - /// The stream. - /// true if the stream is persistent; otherwise false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeMessage Load (Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, stream, persistent, cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save mmeory usage, but also improve - /// performance. - /// - /// The parsed message. - /// The stream. - /// true if the stream is persistent; otherwise false. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, stream, persistent, cancellationToken); - } - - /// - /// Load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// - /// The parsed message. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeMessage Load (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, stream, false, cancellationToken); - } - - /// - /// Asynchronously load a from the specified stream. - /// - /// - /// Loads a from the given stream, using the - /// default . - /// - /// The parsed message. - /// The stream. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (Stream stream, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, stream, false, cancellationToken); - } - - /// - /// Load a from the specified file. - /// - /// - /// Loads a from the file at the given path, using the - /// specified . - /// - /// The parsed message. - /// The parser options. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeMessage Load (ParserOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - return Load (options, stream, cancellationToken); - } - - /// - /// Asynchronously load a from the specified file. - /// - /// - /// Loads a from the file at the given path, using the - /// specified . - /// - /// The parsed message. - /// The parser options. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static async Task LoadAsync (ParserOptions options, string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - using (var stream = File.OpenRead (fileName)) - return await LoadAsync (options, stream, cancellationToken).ConfigureAwait (false); - } - - /// - /// Load a from the specified file. - /// - /// - /// Loads a from the file at the given path, using the - /// default . - /// - /// The parsed message. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static MimeMessage Load (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - return Load (ParserOptions.Default, fileName, cancellationToken); - } - - /// - /// Asynchronously load a from the specified file. - /// - /// - /// Loads a from the file at the given path, using the - /// default . - /// - /// The parsed message. - /// The name of the file to load. - /// The cancellation token. - /// - /// is null. - /// - /// - /// is a zero-length string, contains only white space, or - /// contains one or more invalid characters. - /// - /// - /// is an invalid file path. - /// - /// - /// The specified file path could not be found. - /// - /// - /// The user does not have access to read the specified file. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public static Task LoadAsync (string fileName, CancellationToken cancellationToken = default (CancellationToken)) - { - return LoadAsync (ParserOptions.Default, fileName, cancellationToken); - } - -#if ENABLE_SNM - static MimePart GetMimePart (AttachmentBase item) - { - var mimeType = item.ContentType.ToString (); - var contentType = ContentType.Parse (mimeType); - var attachment = item as Attachment; - MimePart part; - - if (contentType.MediaType.Equals ("text", StringComparison.OrdinalIgnoreCase)) - part = new TextPart (contentType); - else - part = new MimePart (contentType); - - if (attachment != null) { - var disposition = attachment.ContentDisposition.ToString (); - part.ContentDisposition = ContentDisposition.Parse (disposition); - } - - switch (item.TransferEncoding) { - case System.Net.Mime.TransferEncoding.QuotedPrintable: - part.ContentTransferEncoding = ContentEncoding.QuotedPrintable; - break; - case System.Net.Mime.TransferEncoding.Base64: - part.ContentTransferEncoding = ContentEncoding.Base64; - break; - case System.Net.Mime.TransferEncoding.SevenBit: - part.ContentTransferEncoding = ContentEncoding.SevenBit; - break; - //case System.Net.Mime.TransferEncoding.EightBit: - // part.ContentTransferEncoding = ContentEncoding.EightBit; - // break; - } - - if (item.ContentId != null) - part.ContentId = item.ContentId; - - var stream = new MemoryBlockStream (); - if (item.ContentStream.CanSeek) - item.ContentStream.Position = 0; - item.ContentStream.CopyTo (stream); - stream.Position = 0; - - part.Content = new MimeContent (stream); - - return part; - } - - /// - /// Creates a new from a . - /// - /// - /// Creates a new from a . - /// - /// The equivalent . - /// The message. - /// - /// is null. - /// - public static MimeMessage CreateFromMailMessage (MailMessage message) - { - if (message == null) - throw new ArgumentNullException (nameof (message)); - - var headers = new List
(); - foreach (var field in message.Headers.AllKeys) { - foreach (var value in message.Headers.GetValues (field)) - headers.Add (new Header (field, value)); - } - - var msg = new MimeMessage (ParserOptions.Default, headers, RfcComplianceMode.Strict); - MimeEntity body = null; - - // Note: If the user has already sent their MailMessage via System.Net.Mail.SmtpClient, - // then the following MailMessage properties will have been merged into the Headers, so - // check to make sure our MimeMessage properties are empty before adding them. - if (message.Sender != null) - msg.Sender = (MailboxAddress) message.Sender; - - if (message.From != null) { - msg.Headers.Replace (HeaderId.From, string.Empty); - msg.From.Add ((MailboxAddress) message.From); - } - - if (message.ReplyToList.Count > 0) { - msg.Headers.Replace (HeaderId.ReplyTo, string.Empty); - msg.ReplyTo.AddRange ((InternetAddressList) message.ReplyToList); - } - - if (message.To.Count > 0) { - msg.Headers.Replace (HeaderId.To, string.Empty); - msg.To.AddRange ((InternetAddressList) message.To); - } - - if (message.CC.Count > 0) { - msg.Headers.Replace (HeaderId.Cc, string.Empty); - msg.Cc.AddRange ((InternetAddressList) message.CC); - } - - if (message.Bcc.Count > 0) { - msg.Headers.Replace (HeaderId.Bcc, string.Empty); - msg.Bcc.AddRange ((InternetAddressList) message.Bcc); - } - - if (message.SubjectEncoding != null) - msg.Headers.Replace (HeaderId.Subject, message.SubjectEncoding, message.Subject ?? string.Empty); - else - msg.Subject = message.Subject ?? string.Empty; - - if (!msg.Headers.Contains (HeaderId.Date)) - msg.Date = DateTimeOffset.Now; - - switch (message.Priority) { - case MailPriority.Normal: - msg.Headers.RemoveAll (HeaderId.XMSMailPriority); - msg.Headers.RemoveAll (HeaderId.Importance); - msg.Headers.RemoveAll (HeaderId.XPriority); - msg.Headers.RemoveAll (HeaderId.Priority); - break; - case MailPriority.High: - msg.Headers.Replace (HeaderId.Priority, "urgent"); - msg.Headers.Replace (HeaderId.Importance, "high"); - msg.Headers.Replace (HeaderId.XPriority, "2 (High)"); - break; - case MailPriority.Low: - msg.Headers.Replace (HeaderId.Priority, "non-urgent"); - msg.Headers.Replace (HeaderId.Importance, "low"); - msg.Headers.Replace (HeaderId.XPriority, "4 (Low)"); - break; - } - - if (!string.IsNullOrEmpty (message.Body)) { - var text = new TextPart (message.IsBodyHtml ? "html" : "plain"); - text.SetText (message.BodyEncoding ?? Encoding.UTF8, message.Body); - body = text; - } - - if (message.AlternateViews.Count > 0) { - var alternative = new MultipartAlternative (); - - if (body != null) - alternative.Add (body); - - foreach (var view in message.AlternateViews) { - var part = GetMimePart (view); - - if (view.LinkedResources.Count > 0) { - var type = part.ContentType.MediaType + "/" + part.ContentType.MediaSubtype; - var related = new MultipartRelated (); - - related.ContentType.Parameters.Add ("type", type); - related.ContentBase = view.BaseUri; - - related.Add (part); - - foreach (var resource in view.LinkedResources) { - part = GetMimePart (resource); - - if (resource.ContentLink != null) - part.ContentLocation = resource.ContentLink; - - related.Add (part); - } - - alternative.Add (related); - } else { - part.ContentBase = view.BaseUri; - alternative.Add (part); - } - } - - body = alternative; - } - - if (body == null) - body = new TextPart (message.IsBodyHtml ? "html" : "plain"); - - if (message.Attachments.Count > 0) { - var mixed = new Multipart ("mixed"); - - if (body != null) - mixed.Add (body); - - foreach (var attachment in message.Attachments) - mixed.Add (GetMimePart (attachment)); - - body = mixed; - } - - msg.Body = body; - - return msg; - } - - /// - /// Explicit cast to convert a to a - /// . - /// - /// - /// Allows creation of messages using Microsoft's System.Net.Mail APIs. - /// - /// The equivalent . - /// The message. - public static explicit operator MimeMessage (MailMessage message) - { - return message != null ? CreateFromMailMessage (message) : null; - } -#endif - } -} diff --git a/src/MimeKit/MimeParser.cs b/src/MimeKit/MimeParser.cs deleted file mode 100644 index e63d09e..0000000 --- a/src/MimeKit/MimeParser.cs +++ /dev/null @@ -1,2028 +0,0 @@ -// -// MimeParser.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Diagnostics; -using System.Collections; -using System.Collections.Generic; - -using MimeKit.IO; -using MimeKit.Utils; - -namespace MimeKit { - enum BoundaryType - { - None, - Eos, - ImmediateBoundary, - ImmediateEndBoundary, - ParentBoundary, - ParentEndBoundary, - } - - class Boundary - { - public static readonly byte[] MboxFrom = Encoding.ASCII.GetBytes ("From "); - - public byte[] Marker { get; private set; } - public int FinalLength { get { return Marker.Length; } } - public int Length { get; private set; } - public int MaxLength { get; private set; } - - public Boundary (string boundary, int currentMaxLength) - { - Marker = Encoding.UTF8.GetBytes ("--" + boundary + "--"); - Length = Marker.Length - 2; - - MaxLength = Math.Max (currentMaxLength, Marker.Length); - } - - Boundary () - { - } - - public static Boundary CreateMboxBoundary () - { - var boundary = new Boundary (); - boundary.Marker = MboxFrom; - boundary.MaxLength = 5; - boundary.Length = 5; - return boundary; - } - - public override string ToString () - { - return Encoding.UTF8.GetString (Marker, 0, Marker.Length); - } - } - - enum MimeParserState : sbyte - { - Error = -1, - Initialized, - MboxMarker, - MessageHeaders, - Headers, - Content, - Boundary, - Complete, - Eos - } - - /// - /// A MIME message and entity parser. - /// - /// - /// A MIME parser is used to parse and - /// objects from arbitrary streams. - /// - public partial class MimeParser : IEnumerable - { - static readonly byte[] UTF8ByteOrderMark = { 0xEF, 0xBB, 0xBF }; - const int ReadAheadSize = 128; - const int BlockSize = 4096; - const int PadSize = 4; - - // I/O buffering - readonly byte[] input = new byte[ReadAheadSize + BlockSize + PadSize]; - const int inputStart = ReadAheadSize; - int inputIndex = ReadAheadSize; - int inputEnd = ReadAheadSize; - - // mbox From-line state - byte[] mboxMarkerBuffer; - long mboxMarkerOffset; - int mboxMarkerLength; - - // message/rfc822 mbox markers (shouldn't exist, but sometimes do) - byte[] preHeaderBuffer = new byte[128]; - int preHeaderLength; - - // header buffer - byte[] headerBuffer = new byte[512]; - long headerOffset; - int headerIndex; - - readonly List bounds = new List (); - readonly List
headers = new List
(); - - MimeParserState state; - BoundaryType boundary; - MimeFormat format; - bool persistent; - bool toplevel; - bool eos; - - ParserOptions options; - long headerBlockBegin; - long headerBlockEnd; - long contentEnd; - int lineNumber; - Stream stream; - long position; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new that will parse the specified stream. - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save memory usage, but also improve - /// performance. - /// It should be noted, however, that disposing will make it impossible - /// for to read the content. - /// - /// The stream to parse. - /// The format of the stream. - /// true if the stream is persistent; otherwise false. - /// - /// is null. - /// - public MimeParser (Stream stream, MimeFormat format, bool persistent = false) : this (ParserOptions.Default, stream, format, persistent) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new that will parse the specified stream. - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save memory usage, but also improve - /// performance. - /// It should be noted, however, that disposing will make it impossible - /// for to read the content. - /// - /// The stream to parse. - /// true if the stream is persistent; otherwise false. - /// - /// is null. - /// - public MimeParser (Stream stream, bool persistent = false) : this (ParserOptions.Default, stream, MimeFormat.Default, persistent) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new that will parse the specified stream. - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save memory usage, but also improve - /// performance. - /// It should be noted, however, that disposing will make it impossible - /// for to read the content. - /// - /// The parser options. - /// The stream to parse. - /// true if the stream is persistent; otherwise false. - /// - /// is null. - /// -or- - /// is null. - /// - public MimeParser (ParserOptions options, Stream stream, bool persistent = false) : this (options, stream, MimeFormat.Default, persistent) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new that will parse the specified stream. - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save memory usage, but also improve - /// performance. - /// It should be noted, however, that disposing will make it impossible - /// for to read the content. - /// - /// The parser options. - /// The stream to parse. - /// The format of the stream. - /// true if the stream is persistent; otherwise false. - /// - /// is null. - /// -or- - /// is null. - /// - public MimeParser (ParserOptions options, Stream stream, MimeFormat format, bool persistent = false) - { - SetStream (options, stream, format, persistent); - } - - /// - /// Gets a value indicating whether the parser has reached the end of the input stream. - /// - /// - /// Gets a value indicating whether the parser has reached the end of the input stream. - /// - /// true if this parser has reached the end of the input stream; - /// otherwise, false. - public bool IsEndOfStream { - get { return state == MimeParserState.Eos; } - } - - /// - /// Gets the current position of the parser within the stream. - /// - /// - /// Gets the current position of the parser within the stream. - /// - /// The stream offset. - public long Position { - get { return GetOffset (-1); } - } - - /// - /// Gets the most recent mbox marker offset. - /// - /// - /// Gets the most recent mbox marker offset. - /// - /// The mbox marker offset. - public long MboxMarkerOffset { - get { return mboxMarkerOffset; } - } - - /// - /// Gets the most recent mbox marker. - /// - /// - /// Gets the most recent mbox marker. - /// - /// The mbox marker. - public string MboxMarker { - get { return Encoding.UTF8.GetString (mboxMarkerBuffer, 0, mboxMarkerLength); } - } - - /// - /// Sets the stream to parse. - /// - /// - /// Sets the stream to parse. - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save memory usage, but also improve - /// performance. - /// It should be noted, however, that disposing will make it impossible - /// for to read the content. - /// - /// The parser options. - /// The stream to parse. - /// The format of the stream. - /// true if the stream is persistent; otherwise false. - /// - /// is null. - /// -or- - /// is null. - /// - public void SetStream (ParserOptions options, Stream stream, MimeFormat format, bool persistent = false) - { - if (options == null) - throw new ArgumentNullException (nameof (options)); - - if (stream == null) - throw new ArgumentNullException (nameof (stream)); - - this.persistent = persistent && stream.CanSeek; - this.options = options.Clone (); - this.format = format; - this.stream = stream; - - inputIndex = inputStart; - inputEnd = inputStart; - - mboxMarkerOffset = 0; - mboxMarkerLength = 0; - headerBlockBegin = 0; - headerBlockEnd = 0; - lineNumber = 0; - contentEnd = 0; - - position = stream.CanSeek ? stream.Position : 0; - preHeaderLength = 0; - headers.Clear (); - headerOffset = 0; - headerIndex = 0; - toplevel = false; - eos = false; - - bounds.Clear (); - if (format == MimeFormat.Mbox) { - bounds.Add (Boundary.CreateMboxBoundary ()); - - if (mboxMarkerBuffer == null) - mboxMarkerBuffer = new byte[ReadAheadSize]; - } - - state = MimeParserState.Initialized; - boundary = BoundaryType.None; - } - - /// - /// Sets the stream to parse. - /// - /// - /// Sets the stream to parse. - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save memory usage, but also improve - /// performance. - /// It should be noted, however, that disposing will make it impossible - /// for to read the content. - /// - /// The parser options. - /// The stream to parse. - /// true if the stream is persistent; otherwise false. - /// - /// is null. - /// -or- - /// is null. - /// - public void SetStream (ParserOptions options, Stream stream, bool persistent = false) - { - SetStream (options, stream, MimeFormat.Default, persistent); - } - - /// - /// Sets the stream to parse. - /// - /// - /// Sets the stream to parse. - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save memory usage, but also improve - /// performance. - /// It should be noted, however, that disposing will make it impossible - /// for to read the content. - /// - /// The stream to parse. - /// The format of the stream. - /// true if the stream is persistent; otherwise false. - /// - /// is null. - /// - public void SetStream (Stream stream, MimeFormat format, bool persistent = false) - { - SetStream (ParserOptions.Default, stream, format, persistent); - } - - /// - /// Sets the stream to parse. - /// - /// - /// Sets the stream to parse. - /// If is true and is seekable, then - /// the will not copy the content of s into memory. Instead, - /// it will use a to reference a substream of . - /// This has the potential to not only save memory usage, but also improve - /// performance. - /// It should be noted, however, that disposing will make it impossible - /// for to read the content. - /// - /// The stream to parse. - /// true if the stream is persistent; otherwise false. - /// - /// is null. - /// - public void SetStream (Stream stream, bool persistent = false) - { - SetStream (ParserOptions.Default, stream, MimeFormat.Default, persistent); - } - - /// - /// Invoked when an mbox marker is found. - /// - /// - /// Invoked when an mbox marker is found, providing subclasses with the ability to track stream offsets. - /// - /// The stream offset at which the mbox marker begins. - protected virtual void OnMboxMarkerBegin (long offset) - { - } - - /// - /// Invoked when the end of an mbox marker is found. - /// - /// - /// Invoked when the end of an mbox marker is found, providing subclasses with the ability to track stream offsets. - /// - /// The stream offset at which the mbox marker ends. - protected virtual void OnMboxMarkerEnd (long offset) - { - } - - /// - /// Invoked when the beginning of a MIME message is found. - /// - /// - /// Invoked when the beginning of a MIME message is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME message. - /// The stream offset at which the MIME message begins. - protected virtual void OnMimeMessageBegin (MimeMessage message, long offset) - { - } - - /// - /// Invoked when the end of a MIME message is found. - /// - /// - /// Invoked when the end of a MIME message is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME message. - /// The stream offset at which the MIME message ends. - protected virtual void OnMimeMessageEnd (MimeMessage message, long offset) - { - } - - /// - /// Invoked when the end of the message headers is found. - /// - /// - /// Invoked when the end of the message headers is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME message. - /// The stream offset at which the MIME message headers end. - protected virtual void OnMimeMessageHeadersEnd (MimeMessage message, long offset) - { - } - - /// - /// Invoked when the beginning of a MIME entity is found. - /// - /// - /// Invoked when the beginning of a MIME entity is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME entity. - /// The stream offset at which the MIME entity begins. - protected virtual void OnMimeEntityBegin (MimeEntity entity, long offset) - { - } - - /// - /// Invoked when the end of a MIME entity is found. - /// - /// - /// Invoked when the end of a MIME entity is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME entity. - /// The stream offset at which the MIME entity ends. - protected virtual void OnMimeEntityEnd (MimeEntity entity, long offset) - { - } - - /// - /// Invoked when the end of MIME entity headers is found. - /// - /// - /// Invoked when the end of MIME entity headers is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME entity. - /// The stream offset at which the MIME entity ends. - protected virtual void OnMimeEntityHeadersEnd (MimeEntity entity, long offset) - { - } - - /// - /// Invoked when the beginning of a MIME entity's content is found. - /// - /// - /// Invoked when the beginning of a MIME entity's content is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME entity. - /// The stream offset at which the MIME entity's content begins. - protected virtual void OnMimeContentBegin (MimeEntity entity, long offset) - { - } - - /// - /// Invoked when the end of a MIME entity's content is found. - /// - /// - /// Invoked when the end of a MIME entity's content is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME entity. - /// The stream offset at which the MIME entity's content ends. - protected virtual void OnMimeContentEnd (MimeEntity entity, long offset) - { - } - - /// - /// Invoked when a multipart boundary is found. - /// - /// - /// Invoked when a multipart boundary is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME multipart. - /// The stream offset at which the multipart boundary begins. - protected virtual void OnMultipartBoundaryBegin (Multipart multipart, long offset) - { - } - - /// - /// Invoked when the end of a multipart boundary is found. - /// - /// - /// Invoked when the end of a multipart boundary is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME multipart. - /// The stream offset at which the multipart boundary ends. - protected virtual void OnMultipartBoundaryEnd (Multipart multipart, long offset) - { - } - - /// - /// Invoked when a multipart end-boundary is found. - /// - /// - /// Invoked when a multipart end-boundary is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME multipart. - /// The stream offset at which the multipart end-boundary begins. - protected virtual void OnMultipartEndBoundaryBegin (Multipart multipart, long offset) - { - } - - /// - /// Invoked when the end of a multipart end-boundary is found. - /// - /// - /// Invoked when the end of a multipart end-boundary is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME multipart. - /// The stream offset at which the multipart end-boundary ends. - protected virtual void OnMultipartEndBoundaryEnd (Multipart multipart, long offset) - { - } - - /// - /// Invoked when a multipart preamble is found. - /// - /// - /// Invoked when a multipart preamble is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME multipart. - /// The stream offset at which the preamble begins. - protected virtual void OnMultipartPreambleBegin (Multipart multipart, long offset) - { - } - - /// - /// Invoked when the end of a multipart preamble is found. - /// - /// - /// Invoked when the end of a multipart preamble is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME multipart. - /// The stream offset at which the preamble ends. - protected virtual void OnMultipartPreambleEnd (Multipart multipart, long offset) - { - } - - /// - /// Invoked when a multipart epilogue is found. - /// - /// - /// Invoked when a multipart epilogue is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME multipart. - /// The stream offset at which the epilogue begins. - protected virtual void OnMultipartEpilogueBegin (Multipart multipart, long offset) - { - } - - /// - /// Invoked when the end of a multipart epilogue is found. - /// - /// - /// Invoked when the end of a multipart epilogue is found, providing subclasses with the ability to track stream offsets. - /// - /// The MIME multipart. - /// The stream offset at which the epilogue ends. - protected virtual void OnMultipartEpilogueEnd (Multipart multipart, long offset) - { - } - - /// - /// Invoked for all MIME entities once the octet count for the content has been calculated. - /// - /// - /// Invoked for all MIME entities once the octet count for the content has been calculated. - /// - /// The MIME entity. - /// The number of octets contained in the content of the entity. - protected virtual void OnMimeContentOctets (MimeEntity entity, long octets) - { - } - - /// - /// Invoked for all MIME entities once the line count for the content has been calculated. - /// - /// - /// Invoked for all MIME entities once the line count for the content has been calculated. - /// - /// The MIME entity. - /// The number of lines contained in the content of the entity. - protected virtual void OnMimeContentLines (MimeEntity entity, int lines) - { - } - -#if DEBUG - static string ConvertToCString (byte[] buffer, int startIndex, int length) - { - var cstr = new StringBuilder (); - cstr.AppendCString (buffer, startIndex, length); - return cstr.ToString (); - } -#endif - - static int NextAllocSize (int need) - { - return (need + 63) & ~63; - } - - bool AlignReadAheadBuffer (int atleast, int save, out int left, out int start, out int end) - { - left = inputEnd - inputIndex; - start = inputStart; - end = inputEnd; - - if (left >= atleast || eos) - return false; - - left += save; - - if (left > 0) { - int index = inputIndex - save; - - // attempt to align the end of the remaining input with ReadAheadSize - if (index >= start) { - start -= Math.Min (ReadAheadSize, left); - Buffer.BlockCopy (input, index, input, start, left); - index = start; - start += left; - } else if (index > 0) { - int shift = Math.Min (index, end - start); - Buffer.BlockCopy (input, index, input, index - shift, left); - index -= shift; - start = index + left; - } else { - // we can't shift... - start = end; - } - - inputIndex = index + save; - inputEnd = start; - } else { - inputIndex = start; - inputEnd = start; - } - - end = input.Length - PadSize; - - return true; - } - - int ReadAhead (int atleast, int save, CancellationToken cancellationToken) - { - int nread, left, start, end; - - if (!AlignReadAheadBuffer (atleast, save, out left, out start, out end)) - return left; - - // use the cancellable stream interface if available... - var cancellable = stream as ICancellableStream; - if (cancellable != null) { - nread = cancellable.Read (input, start, end - start, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - nread = stream.Read (input, start, end - start); - } - - if (nread > 0) { - inputEnd += nread; - position += nread; - } else { - eos = true; - } - - return inputEnd - inputIndex; - } - - long GetOffset (int index) - { - if (position == -1) - return -1; - - if (index == -1) - index = inputIndex; - - return position - (inputEnd - index); - } - - static unsafe bool CStringsEqual (byte* str1, byte* str2, int length) - { - byte* se = str1 + length; - byte* s1 = str1; - byte* s2 = str2; - - while (s1 < se) { - if (*s1++ != *s2++) - return false; - } - - return true; - } - - unsafe void StepByteOrderMark (byte* inbuf, ref int bomIndex) - { - byte* inptr = inbuf + inputIndex; - byte* inend = inbuf + inputEnd; - - while (inptr < inend && bomIndex < UTF8ByteOrderMark.Length && *inptr == UTF8ByteOrderMark[bomIndex]) { - bomIndex++; - inptr++; - } - - inputIndex = (int) (inptr - inbuf); - } - - unsafe bool StepByteOrderMark (byte* inbuf, CancellationToken cancellationToken) - { - int bomIndex = 0; - - do { - var available = ReadAhead (ReadAheadSize, 0, cancellationToken); - - if (available <= 0) { - // failed to read any data... EOF - inputIndex = inputEnd; - return false; - } - - StepByteOrderMark (inbuf, ref bomIndex); - } while (inputIndex == inputEnd); - - return bomIndex == 0 || bomIndex == UTF8ByteOrderMark.Length; - } - - static unsafe bool IsMboxMarker (byte* text, bool allowMunged = false) - { -#if COMPARE_QWORD - const ulong FromMask = 0x000000FFFFFFFFFF; - const ulong From = 0x000000206D6F7246; - ulong* qword = (ulong*) text; - - return (*qword & FromMask) == From; -#else - byte* inptr = text; - - if (allowMunged && *inptr == (byte) '>') - inptr++; - - return *inptr++ == (byte) 'F' && *inptr++ == (byte) 'r' && *inptr++ == (byte) 'o' && *inptr++ == (byte) 'm' && *inptr == (byte) ' '; -#endif - } - - unsafe void StepMboxMarker (byte *inbuf, ref bool needInput, ref bool complete, ref int left) - { - byte* inptr = inbuf + inputIndex; - byte* inend = inbuf + inputEnd; - - *inend = (byte) '\n'; - - while (inptr < inend) { - byte* start = inptr; - - // scan for the end of the line - while (*inptr != (byte) '\n') - inptr++; - - long length = inptr - start; - - if (inptr > start && *(inptr - 1) == (byte) '\r') - length--; - - // consume the '\n' - inptr++; - - if (inptr >= inend) { - // we don't have enough input data - inputIndex = (int) (start - inbuf); - left = (int) (inptr - start); - needInput = true; - break; - } - - lineNumber++; - - if (length >= 5 && IsMboxMarker (start)) { - int startIndex = (int) (start - inbuf); - - mboxMarkerOffset = GetOffset (startIndex); - mboxMarkerLength = (int) length; - - OnMboxMarkerBegin (mboxMarkerOffset); - OnMboxMarkerEnd (mboxMarkerOffset + length); - - if (mboxMarkerBuffer.Length < mboxMarkerLength) - Array.Resize (ref mboxMarkerBuffer, mboxMarkerLength); - - Buffer.BlockCopy (input, startIndex, mboxMarkerBuffer, 0, (int) length); - complete = true; - break; - } - } - - if (!needInput) { - inputIndex = (int) (inptr - inbuf); - left = 0; - } - } - - unsafe void StepMboxMarker (byte* inbuf, CancellationToken cancellationToken) - { - bool complete = false; - bool needInput; - int left = 0; - - mboxMarkerLength = 0; - - do { - var available = ReadAhead (Math.Max (ReadAheadSize, left), 0, cancellationToken); - - if (available <= left) { - // failed to find a From line; EOF reached - state = MimeParserState.Error; - inputIndex = inputEnd; - return; - } - - needInput = false; - - StepMboxMarker (inbuf, ref needInput, ref complete, ref left); - } while (!complete); - - state = MimeParserState.MessageHeaders; - } - - void AppendRawHeaderData (int startIndex, int length) - { - int left = headerBuffer.Length - headerIndex; - - if (left < length) - Array.Resize (ref headerBuffer, NextAllocSize (headerIndex + length)); - - Buffer.BlockCopy (input, startIndex, headerBuffer, headerIndex, length); - headerIndex += length; - } - - void ResetRawHeaderData () - { - preHeaderLength = 0; - headerIndex = 0; - } - - unsafe void ParseAndAppendHeader () - { - if (headerIndex == 0) - return; - - fixed (byte* buf = headerBuffer) { - if (Header.TryParse (options, buf, headerIndex, false, out var header)) { - header.Offset = headerOffset; - headers.Add (header); - headerIndex = 0; - } - } - } - - static bool IsControl (byte c) - { - return c.IsCtrl (); - } - - static bool IsBlank (byte c) - { - return c.IsBlank (); - } - - static unsafe bool IsEoln (byte *text) - { - if (*text == (byte) '\r') - text++; - - return *text == (byte) '\n'; - } - - unsafe bool StepHeaders (byte* inbuf, ref bool scanningFieldName, ref bool checkFolded, ref bool midline, - ref bool blank, ref bool valid, ref int left) - { - byte* inptr = inbuf + inputIndex; - byte* inend = inbuf + inputEnd; - bool needInput = false; - long length; - bool eoln; - - *inend = (byte) '\n'; - - while (inptr < inend) { - byte* start = inptr; - - // if we are scanning a new line, check for a folded header - if (!midline && checkFolded && !IsBlank (*inptr)) { - ParseAndAppendHeader (); - - headerOffset = GetOffset ((int) (inptr - inbuf)); - scanningFieldName = true; - checkFolded = false; - blank = false; - valid = true; - } - - eoln = IsEoln (inptr); - if (scanningFieldName && !eoln) { - // scan and validate the field name - if (*inptr != (byte) ':') { - *inend = (byte) ':'; - - while (*inptr != (byte) ':') { - // Blank spaces are allowed between the field name and - // the ':', but field names themselves are not allowed - // to contain spaces. - if (IsBlank (*inptr)) { - blank = true; - } else if (blank || IsControl (*inptr)) { - valid = false; - break; - } - - inptr++; - } - - if (inptr == inend) { - // we don't have enough input data; restore state back to the beginning of the line - left = (int) (inend - start); - inputIndex = (int) (start - inbuf); - needInput = true; - break; - } - - *inend = (byte) '\n'; - } else { - valid = false; - } - - if (!valid) { - length = inptr - start; - - if (format == MimeFormat.Mbox && inputIndex >= contentEnd && length >= 5 && IsMboxMarker (start)) { - // we've found the start of the next message... - inputIndex = (int) (start - inbuf); - state = MimeParserState.Complete; - headerIndex = 0; - return false; - } - - if (headers.Count == 0) { - if (state == MimeParserState.MessageHeaders) { - // ignore From-lines that might appear at the start of a message - if (toplevel && (length < 5 || !IsMboxMarker (start, true))) { - // not a From-line... - inputIndex = (int) (start - inbuf); - state = MimeParserState.Error; - headerIndex = 0; - return false; - } - } else if (toplevel && state == MimeParserState.Headers) { - inputIndex = (int) (start - inbuf); - state = MimeParserState.Error; - headerIndex = 0; - return false; - } - } - } - } - - scanningFieldName = false; - - while (*inptr != (byte) '\n') - inptr++; - - if (inptr == inend) { - // we didn't manage to slurp up a full line, save what we have and refill our input buffer - length = inptr - start; - - if (inptr > start) { - // Note: if the last byte we got was a '\r', rewind a byte - inptr--; - if (*inptr == (byte) '\r') - length--; - else - inptr++; - } - - if (length > 0) { - AppendRawHeaderData ((int) (start - inbuf), (int) length); - midline = true; - } - - inputIndex = (int) (inptr - inbuf); - left = (int) (inend - inptr); - needInput = true; - break; - } - - lineNumber++; - - // check to see if we've reached the end of the headers - if (!midline && IsEoln (start)) { - inputIndex = (int) (inptr - inbuf) + 1; - state = MimeParserState.Content; - ParseAndAppendHeader (); - headerIndex = 0; - return false; - } - - length = (inptr + 1) - start; - - if ((boundary = CheckBoundary ((int) (start - inbuf), start, (int) length)) != BoundaryType.None) { - inputIndex = (int) (start - inbuf); - state = MimeParserState.Boundary; - headerIndex = 0; - return false; - } - - if (!valid && headers.Count == 0) { - if (length > 0 && preHeaderLength == 0) { - if (inptr[-1] == (byte) '\r') - length--; - length--; - - preHeaderLength = (int) length; - - if (preHeaderLength > preHeaderBuffer.Length) - Array.Resize (ref preHeaderBuffer, NextAllocSize (preHeaderLength)); - - Buffer.BlockCopy (input, (int) (start - inbuf), preHeaderBuffer, 0, preHeaderLength); - } - scanningFieldName = true; - checkFolded = false; - blank = false; - valid = true; - } else { - AppendRawHeaderData ((int) (start - inbuf), (int) length); - checkFolded = true; - } - - midline = false; - inptr++; - } - - if (!needInput) { - inputIndex = (int) (inptr - inbuf); - left = (int) (inend - inptr); - } - - return true; - } - - unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken) - { - bool scanningFieldName = true; - bool checkFolded = false; - bool midline = false; - bool blank = false; - bool valid = true; - int left = 0; - - headerBlockBegin = GetOffset (inputIndex); - boundary = BoundaryType.None; - ResetRawHeaderData (); - headers.Clear (); - - ReadAhead (ReadAheadSize, 0, cancellationToken); - - do { - if (!StepHeaders (inbuf, ref scanningFieldName, ref checkFolded, ref midline, ref blank, ref valid, ref left)) - break; - - var available = ReadAhead (left + 1, 0, cancellationToken); - - if (available == left) { - // EOF reached before we reached the end of the headers... - if (scanningFieldName && left > 0) { - // EOF reached right in the middle of a header field name. Throw an error. - // - // See private email from Feb 8, 2018 which contained a sample message w/o - // any breaks between the header and message body. The file also did not - // end with a newline sequence. - state = MimeParserState.Error; - } else { - // EOF reached somewhere in the middle of the value. - // - // Append whatever data we've got left and pretend we found the end - // of the header value (and the header block). - // - // For more details, see https://github.com/jstedfast/MimeKit/pull/51 - // and https://github.com/jstedfast/MimeKit/issues/348 - if (left > 0) { - AppendRawHeaderData (inputIndex, left); - inputIndex = inputEnd; - } - - ParseAndAppendHeader (); - - headerBlockEnd = GetOffset (inputIndex); - state = MimeParserState.Content; - } - break; - } - } while (true); - - headerBlockEnd = GetOffset (inputIndex); - } - - unsafe bool SkipLine (byte* inbuf, bool consumeNewLine) - { - byte* inptr = inbuf + inputIndex; - byte* inend = inbuf + inputEnd; - - *inend = (byte) '\n'; - - while (*inptr != (byte) '\n') - inptr++; - - if (inptr < inend) { - inputIndex = (int) (inptr - inbuf); - - if (consumeNewLine) { - inputIndex++; - lineNumber++; - } else if (*(inptr - 1) == (byte) '\r') { - inputIndex--; - } - - return true; - } - - inputIndex = inputEnd; - - return false; - } - - unsafe bool SkipLine (byte* inbuf, bool consumeNewLine, CancellationToken cancellationToken) - { - do { - if (SkipLine (inbuf, consumeNewLine)) - return true; - - if (ReadAhead (ReadAheadSize, 1, cancellationToken) <= 0) - return false; - } while (true); - } - - unsafe MimeParserState Step (byte* inbuf, CancellationToken cancellationToken) - { - switch (state) { - case MimeParserState.Initialized: - if (!StepByteOrderMark (inbuf, cancellationToken)) { - state = MimeParserState.Eos; - break; - } - - state = format == MimeFormat.Mbox ? MimeParserState.MboxMarker : MimeParserState.MessageHeaders; - break; - case MimeParserState.MboxMarker: - StepMboxMarker (inbuf, cancellationToken); - break; - case MimeParserState.MessageHeaders: - case MimeParserState.Headers: - StepHeaders (inbuf, cancellationToken); - toplevel = false; - break; - } - - return state; - } - - ContentType GetContentType (ContentType parent) - { - for (int i = 0; i < headers.Count; i++) { - if (!headers[i].Field.Equals ("Content-Type", StringComparison.OrdinalIgnoreCase)) - continue; - - var rawValue = headers[i].RawValue; - int index = 0; - - if (!ContentType.TryParse (options, rawValue, ref index, rawValue.Length, false, out var type) && type == null) { - // if 'type' is null, then it means that even the mime-type was unintelligible - type = new ContentType ("application", "octet-stream"); - - // attempt to recover any parameters... - while (index < rawValue.Length && rawValue[index] != ';') - index++; - - if (++index < rawValue.Length) { - if (ParameterList.TryParse (options, rawValue, ref index, rawValue.Length, false, out var parameters)) - type.Parameters = parameters; - } - } - - return type; - } - - if (parent == null || !parent.IsMimeType ("multipart", "digest")) - return new ContentType ("text", "plain"); - - return new ContentType ("message", "rfc822"); - } - - unsafe bool IsPossibleBoundary (byte* text, int length) - { - if (length < 2) - return false; - - if (*text == (byte) '-' && *(text + 1) == (byte) '-') - return true; - - if (format == MimeFormat.Mbox && length >= 5 && IsMboxMarker (text)) - return true; - - return false; - } - - static unsafe bool IsBoundary (byte* text, int length, byte[] boundary, int boundaryLength) - { - if (boundaryLength > length) - return false; - - fixed (byte* boundaryptr = boundary) { - // make sure that the text matches the boundary - if (!CStringsEqual (text, boundaryptr, boundaryLength)) - return false; - - // if this is an mbox marker, we're done - if (IsMboxMarker (text)) - return true; - - // the boundary may optionally be followed by lwsp - byte* inptr = text + boundaryLength; - byte* inend = text + length; - - while (inptr < inend) { - if (!(*inptr).IsWhitespace ()) - return false; - - inptr++; - } - } - - return true; - } - - unsafe BoundaryType CheckBoundary (int startIndex, byte* start, int length) - { - int count = bounds.Count; - - if (!IsPossibleBoundary (start, length)) - return BoundaryType.None; - - if (contentEnd > 0) { - // We'll need to special-case checking for the mbox From-marker when respecting Content-Length - count--; - } - - for (int i = 0; i < count; i++) { - var boundary = bounds[i]; - - if (IsBoundary (start, length, boundary.Marker, boundary.FinalLength)) - return i == 0 ? BoundaryType.ImmediateEndBoundary : BoundaryType.ParentEndBoundary; - - if (IsBoundary (start, length, boundary.Marker, boundary.Length)) - return i == 0 ? BoundaryType.ImmediateBoundary : BoundaryType.ParentBoundary; - } - - if (contentEnd > 0) { - // now it is time to check the mbox From-marker for the Content-Length case - long curOffset = GetOffset (startIndex); - var boundary = bounds[count]; - - if (curOffset >= contentEnd && IsBoundary (start, length, boundary.Marker, boundary.Length)) - return BoundaryType.ImmediateEndBoundary; - } - - return BoundaryType.None; - } - - unsafe bool FoundImmediateBoundary (byte* inbuf, bool final) - { - int boundaryLength = final ? bounds[0].FinalLength : bounds[0].Length; - byte* start = inbuf + inputIndex; - byte* inend = inbuf + inputEnd; - byte *inptr = start; - - *inend = (byte) '\n'; - - while (*inptr != (byte) '\n') - inptr++; - - return IsBoundary (start, (int) (inptr - start), bounds[0].Marker, boundaryLength); - } - - int GetMaxBoundaryLength () - { - return bounds.Count > 0 ? bounds[0].MaxLength + 2 : 0; - } - - unsafe void ScanContent (byte* inbuf, ref int contentIndex, ref int nleft, ref bool midline, ref bool[] formats) - { - int length = inputEnd - inputIndex; - byte* inptr = inbuf + inputIndex; - byte* inend = inbuf + inputEnd; - int startIndex = inputIndex; - - contentIndex = inputIndex; - - if (midline && length == nleft) - boundary = BoundaryType.Eos; - - *inend = (byte) '\n'; - - while (inptr < inend) { - // Note: we can always depend on byte[] arrays being 4-byte aligned on 32bit and 64bit architectures - int alignment = (startIndex + 3) & ~3; - byte* aligned = inbuf + alignment; - byte* start = inptr; - byte c = *aligned; - uint mask; - - *aligned = (byte) '\n'; - while (*inptr != (byte) '\n') - inptr++; - *aligned = c; - - if (inptr == aligned && c != (byte) '\n') { - // -funroll-loops, bitches. - uint* dword = (uint*) inptr; - - do { - mask = *dword++ ^ 0x0A0A0A0A; - mask = ((mask - 0x01010101) & (~mask & 0x80808080)); - } while (mask == 0); - - inptr = (byte*) (dword - 1); - while (*inptr != (byte) '\n') - inptr++; - } - - length = (int) (inptr - start); - - if (inptr < inend) { - if ((boundary = CheckBoundary (startIndex, start, length)) != BoundaryType.None) - break; - - if (length > 0 && *(inptr - 1) == (byte) '\r') - formats[(int) NewLineFormat.Dos] = true; - else - formats[(int) NewLineFormat.Unix] = true; - - lineNumber++; - length++; - inptr++; - } else { - // didn't find the end of the line... - midline = true; - - if (boundary == BoundaryType.None) { - // not enough to tell if we found a boundary - break; - } - - if ((boundary = CheckBoundary (startIndex, start, length)) != BoundaryType.None) - break; - } - - startIndex += length; - } - - inputIndex = startIndex; - } - - class ScanContentResult - { - public readonly NewLineFormat? Format; - public readonly bool IsEmpty; - - public ScanContentResult (bool[] formats, bool isEmpty) - { - if (formats[(int) NewLineFormat.Unix] && formats[(int) NewLineFormat.Dos]) - Format = NewLineFormat.Mixed; - else if (formats[(int) NewLineFormat.Unix]) - Format = NewLineFormat.Unix; - else if (formats[(int) NewLineFormat.Dos]) - Format = NewLineFormat.Dos; - else - Format = null; - IsEmpty = isEmpty; - } - } - - unsafe ScanContentResult ScanContent (byte* inbuf, Stream content, bool trimNewLine, CancellationToken cancellationToken) - { - int atleast = Math.Max (ReadAheadSize, GetMaxBoundaryLength ()); - int contentIndex = inputIndex; - var formats = new bool[2]; - bool midline = false; - int nleft; - - do { - if (contentIndex < inputIndex) - content.Write (input, contentIndex, inputIndex - contentIndex); - - nleft = inputEnd - inputIndex; - if (ReadAhead (atleast, 2, cancellationToken) <= 0) { - boundary = BoundaryType.Eos; - contentIndex = inputIndex; - break; - } - - ScanContent (inbuf, ref contentIndex, ref nleft, ref midline, ref formats); - } while (boundary == BoundaryType.None); - - if (contentIndex < inputIndex) - content.Write (input, contentIndex, inputIndex - contentIndex); - - var isEmpty = content.Length == 0; - - if (boundary != BoundaryType.Eos && trimNewLine) { - // the last \r\n belongs to the boundary - if (content.Length > 0) { - if (input[inputIndex - 2] == (byte) '\r') - content.SetLength (content.Length - 2); - else - content.SetLength (content.Length - 1); - } - } - - return new ScanContentResult (formats, isEmpty); - } - - unsafe void ConstructMimePart (MimePart part, byte* inbuf, CancellationToken cancellationToken) - { - long endOffset, beginOffset = GetOffset (inputIndex); - var beginLineNumber = lineNumber; - ScanContentResult result; - Stream content; - - OnMimeContentBegin (part, beginOffset); - - if (persistent) { - using (var measured = new MeasuringStream ()) { - result = ScanContent (inbuf, measured, true, cancellationToken); - endOffset = beginOffset + measured.Length; - } - - content = new BoundStream (stream, beginOffset, endOffset, true); - } else { - content = new MemoryBlockStream (); - result = ScanContent (inbuf, content, true, cancellationToken); - content.Seek (0, SeekOrigin.Begin); - endOffset = beginOffset + content.Length; - } - - OnMimeContentEnd (part, endOffset); - OnMimeContentOctets (part, endOffset - beginOffset); - OnMimeContentLines (part, lineNumber - beginLineNumber); - - if (!result.IsEmpty) - part.Content = new MimeContent (content, part.ContentTransferEncoding) { NewLineFormat = result.Format }; - else - content.Dispose (); - } - - unsafe void ConstructMessagePart (MessagePart rfc822, byte* inbuf, int depth, CancellationToken cancellationToken) - { - var beginOffset = GetOffset (inputIndex); - var beginLineNumber = lineNumber; - - OnMimeContentBegin (rfc822, beginOffset); - - if (bounds.Count > 0) { - int atleast = Math.Max (ReadAheadSize, GetMaxBoundaryLength ()); - - if (ReadAhead (atleast, 0, cancellationToken) <= 0) { - boundary = BoundaryType.Eos; - return; - } - - byte* start = inbuf + inputIndex; - byte* inend = inbuf + inputEnd; - byte* inptr = start; - - *inend = (byte) '\n'; - - while (*inptr != (byte) '\n') - inptr++; - - boundary = CheckBoundary (inputIndex, start, (int) (inptr - start)); - - switch (boundary) { - case BoundaryType.ImmediateEndBoundary: - case BoundaryType.ImmediateBoundary: - case BoundaryType.ParentBoundary: - return; - case BoundaryType.ParentEndBoundary: - // ignore "From " boundaries, broken mailers tend to include these... - if (!IsMboxMarker (start)) - return; - break; - } - } - - // parse the headers... - state = MimeParserState.MessageHeaders; - if (Step (inbuf, cancellationToken) == MimeParserState.Error) { - // Note: this either means that StepHeaders() found the end of the stream - // or an invalid header field name at the start of the message headers, - // which likely means that this is not a valid MIME stream? - boundary = BoundaryType.Eos; - return; - } - - var message = new MimeMessage (options, headers, RfcComplianceMode.Loose); - var type = GetContentType (null); - - if (preHeaderBuffer.Length > 0) { - message.MboxMarker = new byte[preHeaderLength]; - Buffer.BlockCopy (preHeaderBuffer, 0, message.MboxMarker, 0, preHeaderLength); - } - - var entity = options.CreateEntity (type, headers, true, depth); - message.Body = entity; - - OnMimeMessageBegin (message, headerBlockBegin); - OnMimeMessageHeadersEnd (message, headerBlockEnd); - OnMimeEntityBegin (entity, headerBlockBegin); - OnMimeEntityHeadersEnd (entity, headerBlockEnd); - - if (entity is Multipart) - ConstructMultipart ((Multipart) entity, inbuf, depth + 1, cancellationToken); - else if (entity is MessagePart) - ConstructMessagePart ((MessagePart) entity, inbuf, depth + 1, cancellationToken); - else - ConstructMimePart ((MimePart) entity, inbuf, cancellationToken); - - rfc822.Message = message; - - var endOffset = GetOffset (inputIndex); - OnMimeEntityEnd (entity, endOffset); - OnMimeMessageEnd (message, endOffset); - OnMimeContentEnd (rfc822, endOffset); - OnMimeContentOctets (rfc822, endOffset - beginOffset); - OnMimeContentLines (rfc822, lineNumber - beginLineNumber); - } - - unsafe void MultipartScanPreamble (Multipart multipart, byte* inbuf, CancellationToken cancellationToken) - { - using (var memory = new MemoryStream ()) { - long offset = GetOffset (inputIndex); - - OnMultipartPreambleBegin (multipart, offset); - ScanContent (inbuf, memory, false, cancellationToken); - multipart.RawPreamble = memory.ToArray (); - OnMultipartPreambleEnd (multipart, offset + memory.Length); - } - } - - unsafe void MultipartScanEpilogue (Multipart multipart, byte* inbuf, CancellationToken cancellationToken) - { - using (var memory = new MemoryStream ()) { - long offset = GetOffset (inputIndex); - - OnMultipartEpilogueBegin (multipart, offset); - var result = ScanContent (inbuf, memory, true, cancellationToken); - multipart.RawEpilogue = result.IsEmpty ? null : memory.ToArray (); - OnMultipartEpilogueEnd (multipart, offset + memory.Length); - } - } - - unsafe void MultipartScanSubparts (Multipart multipart, byte* inbuf, int depth, CancellationToken cancellationToken) - { - do { - OnMultipartBoundaryBegin (multipart, GetOffset (inputIndex)); - - // skip over the boundary marker - if (!SkipLine (inbuf, true, cancellationToken)) { - OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); - boundary = BoundaryType.Eos; - break; - } - - OnMultipartBoundaryEnd (multipart, GetOffset (inputIndex)); - - // parse the headers - state = MimeParserState.Headers; - if (Step (inbuf, cancellationToken) == MimeParserState.Error) { - boundary = BoundaryType.Eos; - break; - } - - if (state == MimeParserState.Boundary) { - if (headers.Count == 0) { - if (boundary == BoundaryType.ImmediateBoundary) - continue; - break; - } - - // This part has no content, but that will be handled in ConstructMultipart() - // or ConstructMimePart(). - } - - //if (state == ParserState.Complete && headers.Count == 0) - // return BoundaryType.EndBoundary; - - var type = GetContentType (multipart.ContentType); - var entity = options.CreateEntity (type, headers, false, depth); - - OnMimeEntityBegin (entity, headerBlockBegin); - OnMimeEntityHeadersEnd (entity, headerBlockEnd); - - if (entity is Multipart) - ConstructMultipart ((Multipart) entity, inbuf, depth + 1, cancellationToken); - else if (entity is MessagePart) - ConstructMessagePart ((MessagePart) entity, inbuf, depth + 1, cancellationToken); - else - ConstructMimePart ((MimePart) entity, inbuf, cancellationToken); - - OnMimeEntityEnd (entity, GetOffset (inputIndex)); - - multipart.Add (entity); - } while (boundary == BoundaryType.ImmediateBoundary); - } - - void PushBoundary (string boundary) - { - if (bounds.Count > 0) - bounds.Insert (0, new Boundary (boundary, bounds[0].MaxLength)); - else - bounds.Add (new Boundary (boundary, 0)); - } - - void PopBoundary () - { - bounds.RemoveAt (0); - } - - unsafe void ConstructMultipart (Multipart multipart, byte* inbuf, int depth, CancellationToken cancellationToken) - { - var beginOffset = GetOffset (inputIndex); - var beginLineNumber = lineNumber; - var marker = multipart.Boundary; - long endOffset; - - OnMimeContentBegin (multipart, beginOffset); - - if (marker == null) { -#if DEBUG - Debug.WriteLine ("Multipart without a boundary encountered!"); -#endif - - // Note: this will scan all content into the preamble... - MultipartScanPreamble (multipart, inbuf, cancellationToken); - endOffset = GetOffset (inputIndex); - - OnMimeContentEnd (multipart, endOffset); - OnMimeContentOctets (multipart, endOffset - beginOffset); - OnMimeContentLines (multipart, lineNumber - beginLineNumber); - return; - } - - PushBoundary (marker); - - MultipartScanPreamble (multipart, inbuf, cancellationToken); - if (boundary == BoundaryType.ImmediateBoundary) - MultipartScanSubparts (multipart, inbuf, depth, cancellationToken); - - if (boundary == BoundaryType.ImmediateEndBoundary) { - OnMultipartEndBoundaryBegin (multipart, GetOffset (inputIndex)); - - // consume the end boundary and read the epilogue (if there is one) - multipart.WriteEndBoundary = true; - SkipLine (inbuf, false, cancellationToken); - PopBoundary (); - - OnMultipartEndBoundaryEnd (multipart, GetOffset (inputIndex)); - - MultipartScanEpilogue (multipart, inbuf, cancellationToken); - endOffset = GetOffset (inputIndex); - - OnMimeContentEnd (multipart, endOffset); - OnMimeContentOctets (multipart, endOffset - beginOffset); - OnMimeContentLines (multipart, lineNumber - beginLineNumber); - return; - } - - endOffset = GetOffset (inputIndex); - - OnMimeContentEnd (multipart, endOffset); - OnMimeContentOctets (multipart, endOffset - beginOffset); - OnMimeContentLines (multipart, lineNumber - beginLineNumber); - - multipart.WriteEndBoundary = false; - - // We either found the end of the stream or we found a parent's boundary - PopBoundary (); - - if (boundary == BoundaryType.ParentEndBoundary && FoundImmediateBoundary (inbuf, true)) - boundary = BoundaryType.ImmediateEndBoundary; - else if (boundary == BoundaryType.ParentBoundary && FoundImmediateBoundary (inbuf, false)) - boundary = BoundaryType.ImmediateBoundary; - } - - unsafe HeaderList ParseHeaders (byte* inbuf, CancellationToken cancellationToken) - { - state = MimeParserState.Headers; - if (Step (inbuf, cancellationToken) == MimeParserState.Error) - throw new FormatException ("Failed to parse headers."); - - state = eos ? MimeParserState.Eos : MimeParserState.Complete; - - var parsed = new HeaderList (options); - foreach (var header in headers) - parsed.Add (header); - - return parsed; - } - - /// - /// Parses a list of headers from the stream. - /// - /// - /// Parses a list of headers from the stream. - /// - /// The parsed list of headers. - /// The cancellation token. - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the headers. - /// - /// - /// An I/O error occurred. - /// - public HeaderList ParseHeaders (CancellationToken cancellationToken = default (CancellationToken)) - { - unsafe { - fixed (byte* inbuf = input) { - return ParseHeaders (inbuf, cancellationToken); - } - } - } - - unsafe MimeEntity ParseEntity (byte* inbuf, CancellationToken cancellationToken) - { - // Note: if a previously parsed MimePart's content has been read, - // then the stream position will have moved and will need to be - // reset. - if (persistent && stream.Position != position) - stream.Seek (position, SeekOrigin.Begin); - - state = MimeParserState.Headers; - toplevel = true; - - if (Step (inbuf, cancellationToken) == MimeParserState.Error) - throw new FormatException ("Failed to parse entity headers."); - - var type = GetContentType (null); - - // Note: we pass 'false' as the 'toplevel' argument here because - // we want the entity to consume all of the headers. - var entity = options.CreateEntity (type, headers, false, 0); - - OnMimeEntityBegin (entity, headerBlockBegin); - OnMimeEntityHeadersEnd (entity, headerBlockEnd); - - if (entity is Multipart) - ConstructMultipart ((Multipart) entity, inbuf, 0, cancellationToken); - else if (entity is MessagePart) - ConstructMessagePart ((MessagePart) entity, inbuf, 0, cancellationToken); - else - ConstructMimePart ((MimePart) entity, inbuf, cancellationToken); - - OnMimeEntityEnd (entity, GetOffset (inputIndex)); - - if (boundary != BoundaryType.Eos) - state = MimeParserState.Complete; - else - state = MimeParserState.Eos; - - return entity; - } - - /// - /// Parses an entity from the stream. - /// - /// - /// Parses an entity from the stream. - /// - /// The parsed entity. - /// The cancellation token. - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the entity. - /// - /// - /// An I/O error occurred. - /// - public MimeEntity ParseEntity (CancellationToken cancellationToken = default (CancellationToken)) - { - unsafe { - fixed (byte* inbuf = input) { - return ParseEntity (inbuf, cancellationToken); - } - } - } - - unsafe MimeMessage ParseMessage (byte* inbuf, CancellationToken cancellationToken) - { - // Note: if a previously parsed MimePart's content has been read, - // then the stream position will have moved and will need to be - // reset. - if (persistent && stream.Position != position) - stream.Seek (position, SeekOrigin.Begin); - - // scan the from-line if we are parsing an mbox - while (state != MimeParserState.MessageHeaders) { - switch (Step (inbuf, cancellationToken)) { - case MimeParserState.Error: - throw new FormatException ("Failed to find mbox From marker."); - case MimeParserState.Eos: - throw new FormatException ("End of stream."); - } - } - - toplevel = true; - - // parse the headers - if (state < MimeParserState.Content && Step (inbuf, cancellationToken) == MimeParserState.Error) - throw new FormatException ("Failed to parse message headers."); - - var message = new MimeMessage (options, headers, RfcComplianceMode.Loose); - - OnMimeMessageBegin (message, headerBlockBegin); - OnMimeMessageHeadersEnd (message, headerBlockEnd); - - contentEnd = 0; - if (format == MimeFormat.Mbox && options.RespectContentLength) { - for (int i = 0; i < headers.Count; i++) { - if (headers[i].Id != HeaderId.ContentLength) - continue; - - var value = headers[i].RawValue; - int length, index = 0; - - if (!ParseUtils.SkipWhiteSpace (value, ref index, value.Length)) - continue; - - if (!ParseUtils.TryParseInt32 (value, ref index, value.Length, out length)) - continue; - - contentEnd = GetOffset (inputIndex) + length; - break; - } - } - - var type = GetContentType (null); - var entity = options.CreateEntity (type, headers, true, 0); - message.Body = entity; - - OnMimeEntityBegin (entity, headerBlockBegin); - OnMimeEntityHeadersEnd (entity, headerBlockEnd); - - if (entity is Multipart) - ConstructMultipart ((Multipart) entity, inbuf, 0, cancellationToken); - else if (entity is MessagePart) - ConstructMessagePart ((MessagePart) entity, inbuf, 0, cancellationToken); - else - ConstructMimePart ((MimePart) entity, inbuf, cancellationToken); - - var endOffset = GetOffset (inputIndex); - OnMimeEntityEnd (entity, endOffset); - OnMimeMessageEnd (message, endOffset); - - if (boundary != BoundaryType.Eos) { - if (format == MimeFormat.Mbox) - state = MimeParserState.MboxMarker; - else - state = MimeParserState.Complete; - } else { - state = MimeParserState.Eos; - } - - return message; - } - - /// - /// Parses a message from the stream. - /// - /// - /// Parses a message from the stream. - /// - /// The parsed message. - /// The cancellation token. - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// There was an error parsing the message. - /// - /// - /// An I/O error occurred. - /// - public MimeMessage ParseMessage (CancellationToken cancellationToken = default (CancellationToken)) - { - unsafe { - fixed (byte* inbuf = input) { - return ParseMessage (inbuf, cancellationToken); - } - } - } - - #region IEnumerable implementation - - /// - /// Enumerates the messages in the stream. - /// - /// - /// This is mostly useful when parsing mbox-formatted streams. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - while (!IsEndOfStream) - yield return ParseMessage (); - - yield break; - } - - #endregion - - #region IEnumerable implementation - - /// - /// Enumerates the messages in the stream. - /// - /// - /// This is mostly useful when parsing mbox-formatted streams. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - } -} diff --git a/src/MimeKit/MimePart.cs b/src/MimeKit/MimePart.cs deleted file mode 100644 index 39838b2..0000000 --- a/src/MimeKit/MimePart.cs +++ /dev/null @@ -1,761 +0,0 @@ -// -// MimePart.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -using MD5 = System.Security.Cryptography.MD5; - -using MimeKit.IO.Filters; -using MimeKit.Encodings; -using MimeKit.Utils; -using MimeKit.IO; - -namespace MimeKit { - /// - /// A leaf-node MIME part that contains content such as the message body text or an attachment. - /// - /// - /// A leaf-node MIME part that contains content such as the message body text or an attachment. - /// - /// - /// - /// - public class MimePart : MimeEntity - { - static readonly string[] ContentTransferEncodings = { - null, "7bit", "8bit", "binary", "base64", "quoted-printable", "x-uuencode" - }; - ContentEncoding encoding; - string md5sum; - int? duration; - - /// - /// Initialize a new instance of the class - /// based on the . - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MimePart (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class - /// with the specified media type and subtype. - /// - /// - /// Creates a new with the specified media type and subtype. - /// - /// The media type. - /// The media subtype. - /// An array of initialization parameters: headers and part content. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// contains more than one or - /// . - /// -or- - /// contains one or more arguments of an unknown type. - /// - public MimePart (string mediaType, string mediaSubtype, params object[] args) : this (mediaType, mediaSubtype) - { - if (args == null) - throw new ArgumentNullException (nameof (args)); - - IMimeContent content = null; - - foreach (object obj in args) { - if (obj == null || TryInit (obj)) - continue; - - if (obj is IMimeContent co) { - if (content != null) - throw new ArgumentException ("IMimeContent should not be specified more than once."); - - content = co; - continue; - } - - if (obj is Stream stream) { - if (content != null) - throw new ArgumentException ("Stream (used as content) should not be specified more than once."); - - // Use default as specified by ContentObject ctor when building a new MimePart. - content = new MimeContent (stream); - continue; - } - - throw new ArgumentException ("Unknown initialization parameter: " + obj.GetType ()); - } - - if (content != null) - Content = content; - } - - /// - /// Initialize a new instance of the class - /// with the specified media type and subtype. - /// - /// - /// Creates a new with the specified media type and subtype. - /// - /// The media type. - /// The media subtype. - /// - /// is null. - /// -or- - /// is null. - /// - public MimePart (string mediaType, string mediaSubtype) : base (mediaType, mediaSubtype) - { - } - - /// - /// Initialize a new instance of the class - /// with the specified content type. - /// - /// - /// Creates a new with the specified Content-Type value. - /// - /// The content type. - /// - /// is null. - /// - public MimePart (ContentType contentType) : base (contentType) - { - } - - /// - /// Initialize a new instance of the class - /// with the specified content type. - /// - /// - /// Creates a new with the specified Content-Type value. - /// - /// The content type. - /// - /// is null. - /// - /// - /// could not be parsed. - /// - public MimePart (string contentType) : base (ContentType.Parse (contentType)) - { - } - - /// - /// Initialize a new instance of the class - /// with the default Content-Type of application/octet-stream. - /// - /// - /// Creates a new with a Content-Type of application/octet-stream. - /// - public MimePart () : this ("application", "octet-stream") - { - } - - /// - /// Gets or sets the duration of the content if available. - /// - /// - /// The Content-Duration header specifies duration of timed media, - /// such as audio or video, in seconds. - /// - /// The duration of the content. - /// - /// is negative. - /// - public int? ContentDuration { - get { return duration; } - set { - if (duration == value) - return; - - if (value.HasValue && value.Value < 0) - throw new ArgumentOutOfRangeException (nameof (value)); - - duration = value; - - if (value.HasValue) - SetHeader ("Content-Duration", value.Value.ToString ()); - else - RemoveHeader ("Content-Duration"); - } - } - - /// - /// Gets or sets the md5sum of the content. - /// - /// - /// The Content-MD5 header specifies the base64-encoded MD5 checksum of the content - /// in its canonical format. - /// For more information, see rfc1864. - /// - /// The md5sum of the content. - public string ContentMd5 { - get { return md5sum; } - set { - if (md5sum == value) - return; - - md5sum = value != null ? value.Trim () : null; - - if (value != null) - SetHeader ("Content-Md5", md5sum); - else - RemoveHeader ("Content-Md5"); - } - } - - /// - /// Gets or sets the content transfer encoding. - /// - /// - /// The Content-Transfer-Encoding header specifies an auxiliary encoding - /// that was applied to the content in order to allow it to pass through - /// mail transport mechanisms (such as SMTP) which may have limitations - /// in the byte ranges that it accepts. For example, many SMTP servers - /// do not accept data outside of the 7-bit ASCII range and so sending - /// binary attachments or even non-English text is not possible without - /// applying an encoding such as base64 or quoted-printable. - /// - /// The content transfer encoding. - /// - /// is not a valid content encoding. - /// - public ContentEncoding ContentTransferEncoding { - get { return encoding; } - set { - if (encoding == value) - return; - - int index = (int) value; - - if (index < 0 || index >= ContentTransferEncodings.Length) - throw new ArgumentOutOfRangeException (nameof (value)); - - var text = ContentTransferEncodings[index]; - - encoding = value; - - if (text != null) - SetHeader ("Content-Transfer-Encoding", text); - else - RemoveHeader ("Content-Transfer-Encoding"); - } - } - - /// - /// Gets or sets the name of the file. - /// - /// - /// First checks for the "filename" parameter on the Content-Disposition header. If - /// that does not exist, then the "name" parameter on the Content-Type header is used. - /// When setting the filename, both the "filename" parameter on the Content-Disposition - /// header and the "name" parameter on the Content-Type header are set. - /// - /// - /// - /// - /// The name of the file. - public string FileName { - get { - string filename = null; - - if (ContentDisposition != null) - filename = ContentDisposition.FileName; - - if (filename == null) - filename = ContentType.Name; - - return filename != null ? filename.Trim () : null; - } - set { - if (value != null) { - if (ContentDisposition == null) - ContentDisposition = new ContentDisposition (ContentDisposition.Attachment); - ContentDisposition.FileName = value; - } else if (ContentDisposition != null) { - ContentDisposition.FileName = value; - } - - ContentType.Name = value; - } - } - - /// - /// Gets or sets the MIME content. - /// - /// - /// Gets or sets the MIME content. - /// - /// - /// - /// - /// The MIME content. - public IMimeContent Content { - get; set; - } - - /// - /// Gets or sets the MIME content. - /// - /// - /// Gets or sets the MIME content. - /// - /// The MIME content. - [Obsolete ("Use the Content property instead.")] - public IMimeContent ContentObject { - get { return Content; } - set { Content = value; } - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMimePart (this); - } - - /// - /// Calculates the most efficient content encoding given the specified constraint. - /// - /// - /// If no is set, will be returned. - /// - /// The most efficient content encoding. - /// The encoding constraint. - /// The cancellation token. - /// - /// is not a valid value. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public ContentEncoding GetBestEncoding (EncodingConstraint constraint, CancellationToken cancellationToken = default (CancellationToken)) - { - return GetBestEncoding (constraint, 78, cancellationToken); - } - - /// - /// Calculates the most efficient content encoding given the specified constraint. - /// - /// - /// If no is set, will be returned. - /// - /// The most efficient content encoding. - /// The encoding constraint. - /// The maximum allowable length for a line (not counting the CRLF). Must be between 72 and 998 (inclusive). - /// The cancellation token. - /// - /// is not between 72 and 998 (inclusive). - /// -or- - /// is not a valid value. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public ContentEncoding GetBestEncoding (EncodingConstraint constraint, int maxLineLength, CancellationToken cancellationToken = default (CancellationToken)) - { - if (Content == null) - return ContentEncoding.SevenBit; - - using (var measure = new MeasuringStream ()) { - using (var filtered = new FilteredStream (measure)) { - var filter = new BestEncodingFilter (); - - filtered.Add (filter); - Content.DecodeTo (filtered, cancellationToken); - filtered.Flush (); - - return filter.GetBestEncoding (constraint, maxLineLength); - } - } - } - - /// - /// Computes the MD5 checksum of the content. - /// - /// - /// Computes the MD5 checksum of the MIME content in its canonical - /// format and then base64-encodes the result. - /// - /// The md5sum of the content. - /// - /// The is null. - /// - public string ComputeContentMd5 () - { - if (Content == null) - throw new InvalidOperationException ("Cannot compute Md5 checksum without a ContentObject."); - - using (var stream = Content.Open ()) { - byte[] checksum; - - using (var filtered = new FilteredStream (stream)) { - if (ContentType.IsMimeType ("text", "*")) - filtered.Add (new Unix2DosFilter ()); - - using (var md5 = MD5.Create ()) - checksum = md5.ComputeHash (filtered); - } - - var base64 = new Base64Encoder (true); - var digest = new byte[base64.EstimateOutputLength (checksum.Length)]; - int n = base64.Flush (checksum, 0, checksum.Length, digest); - - return Encoding.ASCII.GetString (digest, 0, n); - } - } - - static bool IsNullOrWhiteSpace (string value) - { - if (string.IsNullOrEmpty (value)) - return true; - - for (int i = 0; i < value.Length; i++) { - if (!char.IsWhiteSpace (value[i])) - return false; - } - - return true; - } - - /// - /// Verifies the Content-Md5 value against an independently computed md5sum. - /// - /// - /// Computes the MD5 checksum of the MIME content and compares it with the - /// value in the Content-MD5 header, returning true if and only if - /// the values match. - /// - /// true, if content MD5 checksum was verified, false otherwise. - public bool VerifyContentMd5 () - { - if (IsNullOrWhiteSpace (md5sum) || Content == null) - return false; - - return md5sum == ComputeContentMd5 (); - } - - /// - /// Prepare the MIME entity for transport using the specified encoding constraints. - /// - /// - /// Prepares the MIME entity for transport using the specified encoding constraints. - /// - /// The encoding constraint. - /// The maximum number of octets allowed per line (not counting the CRLF). Must be between 60 and 998 (inclusive). - /// - /// is not between 60 and 998 (inclusive). - /// -or- - /// is not a valid value. - /// - public override void Prepare (EncodingConstraint constraint, int maxLineLength = 78) - { - if (maxLineLength < FormatOptions.MinimumLineLength || maxLineLength > FormatOptions.MaximumLineLength) - throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - - switch (ContentTransferEncoding) { - case ContentEncoding.QuotedPrintable: - case ContentEncoding.UUEncode: - case ContentEncoding.Base64: - // these are all safe no matter what the constraints are - return; - case ContentEncoding.Binary: - if (constraint == EncodingConstraint.None) { - // no need to re-encode anything - return; - } - break; - } - - var best = GetBestEncoding (constraint, maxLineLength); - - if (ContentTransferEncoding == ContentEncoding.Default && best == ContentEncoding.SevenBit) - return; - - ContentTransferEncoding = best; - } - - /// - /// Write the to the specified output stream. - /// - /// - /// Writes the MIME part to the output stream. - /// - /// The formatting options. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override void WriteTo (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - base.WriteTo (options, stream, contentOnly, cancellationToken); - - if (Content == null) - return; - - var cancellable = stream as ICancellableStream; - - if (Content.Encoding != encoding) { - if (encoding == ContentEncoding.UUEncode) { - var begin = string.Format ("begin 0644 {0}", FileName ?? "unknown"); - var buffer = Encoding.UTF8.GetBytes (begin); - - if (cancellable != null) { - cancellable.Write (buffer, 0, buffer.Length, cancellationToken); - cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (buffer, 0, buffer.Length); - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - } - - // transcode the content into the desired Content-Transfer-Encoding - using (var filtered = new FilteredStream (stream)) { - filtered.Add (EncoderFilter.Create (encoding)); - - if (encoding != ContentEncoding.Binary) - filtered.Add (options.CreateNewLineFilter (EnsureNewLine)); - - Content.DecodeTo (filtered, cancellationToken); - filtered.Flush (cancellationToken); - } - - if (encoding == ContentEncoding.UUEncode) { - var buffer = Encoding.ASCII.GetBytes ("end"); - - if (cancellable != null) { - cancellable.Write (buffer, 0, buffer.Length, cancellationToken); - cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (buffer, 0, buffer.Length); - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - } - } else if (encoding == ContentEncoding.Binary) { - // Do not alter binary content. - Content.WriteTo (stream, cancellationToken); - } else if (options.VerifyingSignature && Content.NewLineFormat.HasValue && Content.NewLineFormat.Value == NewLineFormat.Mixed) { - // Allow pass-through of the original parsed content without canonicalization when verifying signatures - // if the content contains a mix of line-endings. - // - // See https://github.com/jstedfast/MimeKit/issues/569 for details. - Content.WriteTo (stream, cancellationToken); - } else { - using (var filtered = new FilteredStream (stream)) { - // Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that - // MimeMessage.WriteTo() *always* ends with a new-line. - filtered.Add (options.CreateNewLineFilter (EnsureNewLine)); - Content.WriteTo (filtered, cancellationToken); - filtered.Flush (cancellationToken); - } - } - } - - /// - /// Asynchronously write the to the specified output stream. - /// - /// - /// Asynchronously writes the MIME part to the output stream. - /// - /// An awaitable task. - /// The formatting options. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task WriteToAsync (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - await base.WriteToAsync (options, stream, contentOnly, cancellationToken).ConfigureAwait (false); - - if (Content == null) - return; - - var isText = ContentType.IsMimeType ("text", "*") || ContentType.IsMimeType ("message", "*"); - - if (Content.Encoding != encoding) { - if (encoding == ContentEncoding.UUEncode) { - var begin = string.Format ("begin 0644 {0}", FileName ?? "unknown"); - var buffer = Encoding.UTF8.GetBytes (begin); - - await stream.WriteAsync (buffer, 0, buffer.Length, cancellationToken).ConfigureAwait (false); - await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); - } - - // transcode the content into the desired Content-Transfer-Encoding - using (var filtered = new FilteredStream (stream)) { - filtered.Add (EncoderFilter.Create (encoding)); - - if (encoding != ContentEncoding.Binary) - filtered.Add (options.CreateNewLineFilter (EnsureNewLine)); - - await Content.DecodeToAsync (filtered, cancellationToken).ConfigureAwait (false); - await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); - } - - if (encoding == ContentEncoding.UUEncode) { - var buffer = Encoding.ASCII.GetBytes ("end"); - - await stream.WriteAsync (buffer, 0, buffer.Length, cancellationToken).ConfigureAwait (false); - await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); - } - } else if (encoding == ContentEncoding.Binary) { - // Do not alter binary content. - await Content.WriteToAsync (stream, cancellationToken).ConfigureAwait (false); - } else if (options.VerifyingSignature && Content.NewLineFormat.HasValue && Content.NewLineFormat.Value == NewLineFormat.Mixed) { - // Allow pass-through of the original parsed content without canonicalization when verifying signatures - // if the content contains a mix of line-endings. - // - // See https://github.com/jstedfast/MimeKit/issues/569 for details. - await Content.WriteToAsync (stream, cancellationToken).ConfigureAwait (false); - } else { - using (var filtered = new FilteredStream (stream)) { - // Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that - // MimeMessage.WriteTo() *always* ends with a new-line. - filtered.Add (options.CreateNewLineFilter (EnsureNewLine)); - await Content.WriteToAsync (filtered, cancellationToken).ConfigureAwait (false); - await filtered.FlushAsync (cancellationToken).ConfigureAwait (false); - } - } - } - - /// - /// Called when the headers change in some way. - /// - /// - /// Updates the , , - /// and properties if the corresponding headers have changed. - /// - /// The type of change. - /// The header being added, changed or removed. - protected override void OnHeadersChanged (HeaderListChangedAction action, Header header) - { - int value; - - base.OnHeadersChanged (action, header); - - switch (action) { - case HeaderListChangedAction.Added: - case HeaderListChangedAction.Changed: - switch (header.Id) { - case HeaderId.ContentTransferEncoding: - MimeUtils.TryParse (header.Value, out encoding); - break; - case HeaderId.ContentDuration: - if (int.TryParse (header.Value, out value)) - duration = value; - else - duration = null; - break; - case HeaderId.ContentMd5: - md5sum = header.Value.Trim (); - break; - } - break; - case HeaderListChangedAction.Removed: - switch (header.Id) { - case HeaderId.ContentTransferEncoding: - encoding = ContentEncoding.Default; - break; - case HeaderId.ContentDuration: - duration = null; - break; - case HeaderId.ContentMd5: - md5sum = null; - break; - } - break; - case HeaderListChangedAction.Cleared: - encoding = ContentEncoding.Default; - duration = null; - md5sum = null; - break; - default: - throw new ArgumentOutOfRangeException (nameof (action)); - } - } - } -} diff --git a/src/MimeKit/MimeTypes.cs b/src/MimeKit/MimeTypes.cs deleted file mode 100644 index 957248b..0000000 --- a/src/MimeKit/MimeTypes.cs +++ /dev/null @@ -1,1074 +0,0 @@ -// -// MimeTypes.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A mapping of file name extensions to the corresponding MIME-type. - /// - /// - /// A mapping of file name extensions to the corresponding MIME-type. - /// - public static class MimeTypes - { - static readonly Dictionary extensions; - static readonly Dictionary mimeTypes; - - static MimeTypes () - { - extensions = LoadExtensions (); - mimeTypes = LoadMimeTypes (); - } - - static Dictionary LoadMimeTypes () - { - return new Dictionary (MimeUtils.OrdinalIgnoreCase) { - { ".323", "text/h323" }, - { ".3g2", "video/3gpp2" }, - { ".3gp", "video/3gpp" }, - { ".7z", "application/x-7z-compressed" }, - { ".aab", "application/x-authorware-bin" }, - { ".aac", "audio/aac" }, - { ".aam", "application/x-authorware-map" }, - { ".aas", "application/x-authorware-seg" }, - { ".abc", "text/vnd.abc" }, - { ".acgi", "text/html" }, - { ".acx", "application/internet-property-stream" }, - { ".afl", "video/animaflex" }, - { ".ai", "application/postscript" }, - { ".aif", "audio/aiff" }, - { ".aifc", "audio/aiff" }, - { ".aiff", "audio/aiff" }, - { ".aim", "application/x-aim" }, - { ".aip", "text/x-audiosoft-intra" }, - { ".ani", "application/x-navi-animation" }, - { ".aos", "application/x-nokia-9000-communicator-add-on-software" }, - { ".appcache", "text/cache-manifest" }, - { ".application", "application/x-ms-application" }, - { ".aps", "application/mime" }, - { ".art", "image/x-jg" }, - { ".asf", "video/x-ms-asf" }, - { ".asm", "text/x-asm" }, - { ".asp", "text/asp" }, - { ".asr", "video/x-ms-asf" }, - { ".asx", "application/x-mplayer2" }, - { ".atom", "application/atom+xml" }, - { ".au", "audio/x-au" }, - { ".avi", "video/avi" }, - { ".avs", "video/avs-video" }, - { ".axs", "application/olescript" }, - { ".bas", "text/plain" }, - { ".bcpio", "application/x-bcpio" }, - { ".bin", "application/octet-stream" }, - { ".bm", "image/bmp" }, - { ".bmp", "image/bmp" }, - { ".boo", "application/book" }, - { ".book", "application/book" }, - { ".boz", "application/x-bzip2" }, - { ".bsh", "application/x-bsh" }, - { ".bz2", "application/x-bzip2" }, - { ".bz", "application/x-bzip" }, - { ".cat", "application/vnd.ms-pki.seccat" }, - { ".ccad", "application/clariscad" }, - { ".cco", "application/x-cocoa" }, - { ".cc", "text/plain" }, - { ".cdf", "application/cdf" }, - { ".cer", "application/pkix-cert" }, - { ".cha", "application/x-chat" }, - { ".chat", "application/x-chat" }, - { ".chm", "application/vnd.ms-htmlhelp" }, - { ".class", "application/x-java-applet" }, - { ".clp", "application/x-msclip" }, - { ".cmx", "image/x-cmx" }, - { ".cod", "image/cis-cod" }, - { ".coffee", "text/x-coffeescript" }, - { ".conf", "text/plain" }, - { ".cpio", "application/x-cpio" }, - { ".cpp", "text/plain" }, - { ".cpt", "application/x-cpt" }, - { ".crd", "application/x-mscardfile" }, - { ".crl", "application/pkix-crl" }, - { ".crt", "application/pkix-cert" }, - { ".csh", "application/x-csh" }, - { ".css", "text/css" }, - { ".csv", "text/csv" }, - { ".cs", "text/plain" }, - { ".c", "text/plain" }, - { ".c++", "text/plain" }, - { ".cxx", "text/plain" }, - { ".dart", "application/dart" }, - { ".dcr", "application/x-director" }, - { ".deb", "application/x-deb" }, - { ".deepv", "application/x-deepv" }, - { ".def", "text/plain" }, - { ".deploy", "application/octet-stream" }, - { ".der", "application/x-x509-ca-cert" }, - { ".dib", "image/bmp" }, - { ".dif", "video/x-dv" }, - { ".dir", "application/x-director" }, - { ".disco", "text/xml" }, - { ".dll", "application/x-msdownload" }, - { ".dl", "video/dl" }, - { ".doc", "application/msword" }, - { ".docm", "application/vnd.ms-word.document.macroEnabled.12" }, - { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }, - { ".dot", "application/msword" }, - { ".dotm", "application/vnd.ms-word.template.macroEnabled.12" }, - { ".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, - { ".dp", "application/commonground" }, - { ".drw", "application/drafting" }, - { ".dtd", "application/xml-dtd" }, - { ".dvi", "application/x-dvi" }, - { ".dv", "video/x-dv" }, - { ".dwg", "application/acad" }, - { ".dxf", "application/dxf" }, - { ".dxr", "application/x-director" }, - { ".el", "text/x-script.elisp" }, - { ".elc", "application/x-elc" }, - { ".eml", "message/rfc822" }, - { ".eot", "application/vnd.bw-fontobject" }, - { ".eps", "application/postscript" }, - { ".epub", "application/epub+zip" }, - { ".es", "application/x-esrehber" }, - { ".etx", "text/x-setext" }, - { ".evy", "application/envoy" }, - { ".exe", "application/octet-stream" }, - { ".f77", "text/plain" }, - { ".f90", "text/plain" }, - { ".fdf", "application/vnd.fdf" }, - { ".fif", "image/fif" }, - { ".flac", "audio/x-flac" }, - { ".fli", "video/fli" }, - { ".flx", "text/vnd.fmi.flexstor" }, - { ".fmf", "video/x-atomic3d-feature" }, - { ".for", "text/plain" }, - { ".fpx", "image/vnd.fpx" }, - { ".frl", "application/freeloader" }, - { ".fsx", "application/fsharp-script" }, - { ".g3", "image/g3fax" }, - { ".gif", "image/gif" }, - { ".gl", "video/gl" }, - { ".gsd", "audio/x-gsm" }, - { ".gsm", "audio/x-gsm" }, - { ".gsp", "application/x-gsp" }, - { ".gss", "application/x-gss" }, - { ".gtar", "application/x-gtar" }, - { ".g", "text/plain" }, - { ".gz", "application/x-gzip" }, - { ".gzip", "application/x-gzip" }, - { ".hdf", "application/x-hdf" }, - { ".help", "application/x-helpfile" }, - { ".hgl", "application/vnd.hp-HPGL" }, - { ".hh", "text/plain" }, - { ".hlb", "text/x-script" }, - { ".hlp", "application/x-helpfile" }, - { ".hpg", "application/vnd.hp-HPGL" }, - { ".hpgl", "application/vnd.hp-HPGL" }, - { ".hqx", "application/binhex" }, - { ".hta", "application/hta" }, - { ".htc", "text/x-component" }, - { ".h", "text/plain" }, - { ".htmls", "text/html" }, - { ".html", "text/html" }, - { ".htm", "text/html" }, - { ".htt", "text/webviewhtml" }, - { ".htx", "text/html" }, - { ".ico", "image/x-icon" }, - { ".ics", "text/calendar" }, - { ".idc", "text/plain" }, - { ".ief", "image/ief" }, - { ".iefs", "image/ief" }, - { ".iges", "model/iges" }, - { ".igs", "model/iges" }, - { ".iii", "application/x-iphone" }, - { ".ima", "application/x-ima" }, - { ".imap", "application/x-httpd-imap" }, - { ".inf", "application/inf" }, - { ".ins", "application/x-internett-signup" }, - { ".ip", "application/x-ip2" }, - { ".isp", "application/x-internet-signup" }, - { ".isu", "video/x-isvideo" }, - { ".it", "audio/it" }, - { ".iv", "application/x-inventor" }, - { ".ivf", "video/x-ivf" }, - { ".ivy", "application/x-livescreen" }, - { ".jam", "audio/x-jam" }, - { ".jar", "application/java-archive" }, - { ".java", "text/plain" }, - { ".jav", "text/plain" }, - { ".jcm", "application/x-java-commerce" }, - { ".jfif", "image/jpeg" }, - { ".jfif-tbnl", "image/jpeg" }, - { ".jpeg", "image/jpeg" }, - { ".jpe", "image/jpeg" }, - { ".jpg", "image/jpeg" }, - { ".jps", "image/x-jps" }, - { ".js", "application/javascript" }, - { ".json", "application/json" }, - { ".jut", "image/jutvision" }, - { ".kar", "audio/midi" }, - { ".ksh", "text/x-script.ksh" }, - { ".la", "audio/nspaudio" }, - { ".lam", "audio/x-liveaudio" }, - { ".latex", "application/x-latex" }, - { ".list", "text/plain" }, - { ".lma", "audio/nspaudio" }, - { ".log", "text/plain" }, - { ".lsp", "application/x-lisp" }, - { ".lst", "text/plain" }, - { ".lsx", "text/x-la-asf" }, - { ".ltx", "application/x-latex" }, - { ".m13", "application/x-msmediaview" }, - { ".m14", "application/x-msmediaview" }, - { ".m1v", "video/mpeg" }, - { ".m2a", "audio/mpeg" }, - { ".m2v", "video/mpeg" }, - { ".m3u", "audio/x-mpequrl" }, - { ".m4a", "audio/mp4" }, - { ".m4v", "video/mp4" }, - { ".man", "application/x-troff-man" }, - { ".manifest", "application/x-ms-manifest" }, - { ".map", "application/x-navimap" }, - { ".mar", "text/plain" }, - { ".markdown", "text/markdown" }, - { ".mbd", "application/mbedlet" }, - { ".mc$", "application/x-magic-cap-package-1.0" }, - { ".mcd", "application/mcad" }, - { ".mcf", "image/vasa" }, - { ".mcp", "application/netmc" }, - { ".md", "text/markdown" }, - { ".mdb", "application/x-msaccess" }, - { ".mesh", "model/mesh" }, - { ".me", "application/x-troff-me" }, - { ".mid", "audio/midi" }, - { ".midi", "audio/midi" }, - { ".mif", "application/x-mif" }, - { ".mjf", "audio/x-vnd.AudioExplosion.MjuiceMediaFile" }, - { ".mjpg", "video/x-motion-jpeg" }, - { ".mm", "application/base64" }, - { ".mme", "application/base64" }, - { ".mny", "application/x-msmoney" }, - { ".mod", "audio/mod" }, - { ".mov", "video/quicktime" }, - { ".movie", "video/x-sgi-movie" }, - { ".mp2", "video/mpeg" }, - { ".mp3", "audio/mpeg" }, - { ".mp4", "video/mp4" }, - { ".mp4a", "audio/mp4" }, - { ".mp4v", "video/mp4" }, - { ".mpa", "audio/mpeg" }, - { ".mpc", "application/x-project" }, - { ".mpeg", "video/mpeg" }, - { ".mpe", "video/mpeg" }, - { ".mpga", "audio/mpeg" }, - { ".mpg", "video/mpeg" }, - { ".mpp", "application/vnd.ms-project" }, - { ".mpt", "application/x-project" }, - { ".mpv2", "video/mpeg" }, - { ".mpv", "application/x-project" }, - { ".mpx", "application/x-project" }, - { ".mrc", "application/marc" }, - { ".ms", "application/x-troff-ms" }, - { ".msh", "model/mesh" }, - { ".m", "text/plain" }, - { ".mvb", "application/x-msmediaview" }, - { ".mv", "video/x-sgi-movie" }, - { ".mzz", "application/x-vnd.AudioExplosion.mzz" }, - { ".nap", "image/naplps" }, - { ".naplps", "image/naplps" }, - { ".nc", "application/x-netcdf" }, - { ".ncm", "application/vnd.nokia.configuration-message" }, - { ".niff", "image/x-niff" }, - { ".nif", "image/x-niff" }, - { ".nix", "application/x-mix-transfer" }, - { ".nsc", "application/x-conference" }, - { ".nvd", "application/x-navidoc" }, - { ".nws", "message/rfc822" }, - { ".oda", "application/oda" }, - { ".ods", "application/oleobject" }, - { ".oga", "audio/ogg" }, - { ".ogg", "audio/ogg" }, - { ".ogv", "video/ogg" }, - { ".ogx", "application/ogg" }, - { ".omc", "application/x-omc" }, - { ".omcd", "application/x-omcdatamaker" }, - { ".omcr", "application/x-omcregerator" }, - { ".opus", "audio/ogg" }, - { ".otf", "font/otf" }, - { ".oxps", "application/oxps" }, - { ".p10", "application/pkcs10" }, - { ".p12", "application/pkcs-12" }, - { ".p7a", "application/x-pkcs7-signature" }, - { ".p7b", "application/x-pkcs7-certificates" }, - { ".p7c", "application/pkcs7-mime" }, - { ".p7m", "application/pkcs7-mime" }, - { ".p7r", "application/x-pkcs7-certreqresp" }, - { ".p7s", "application/pkcs7-signature" }, - { ".part", "application/pro_eng" }, - { ".pas", "text/pascal" }, - { ".pbm", "image/x-portable-bitmap" }, - { ".pcl", "application/x-pcl" }, - { ".pct", "image/x-pict" }, - { ".pcx", "image/x-pcx" }, - { ".pdf", "application/pdf" }, - { ".pfx", "application/x-pkcs12" }, - { ".pgm", "image/x-portable-graymap" }, - { ".pic", "image/pict" }, - { ".pict", "image/pict" }, - { ".pkg", "application/x-newton-compatible-pkg" }, - { ".pko", "application/vnd.ms-pki.pko" }, - { ".pl", "text/plain" }, - { ".plx", "application/x-PiXCLscript" }, - { ".pm4", "application/x-pagemaker" }, - { ".pm5", "application/x-pagemaker" }, - { ".pma", "application/x-perfmon" }, - { ".pmc", "application/x-perfmon" }, - { ".pm", "image/x-xpixmap" }, - { ".pml", "application/x-perfmon" }, - { ".pmr", "application/x-perfmon" }, - { ".pmw", "application/x-perfmon" }, - { ".png", "image/png" }, - { ".pnm", "application/x-portable-anymap" }, - { ".pot", "application/vnd.ms-powerpoint" }, - { ".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12" }, - { ".potx", "application/vnd.openxmlformats-officedocument.presentationml.template" }, - { ".pov", "model/x-pov" }, - { ".ppa", "application/vnd.ms-powerpoint" }, - { ".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12" }, - { ".ppm", "image/x-portable-pixmap" }, - { ".pps", "application/vnd.ms-powerpoint" }, - { ".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" }, - { ".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" }, - { ".ppt", "application/vnd.ms-powerpoint" }, - { ".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12" }, - { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" }, - { ".ppz", "application/mspowerpoint" }, - { ".pre", "application/x-freelance" }, - { ".prf", "application/pics-rules" }, - { ".prt", "application/pro_eng" }, - { ".ps", "application/postscript" }, - { ".p", "text/x-pascal" }, - { ".pub", "application/x-mspublisher" }, - { ".pwz", "application/vnd.ms-powerpoint" }, - { ".pyc", "application/x-bytecode.python" }, - { ".py", "text/x-script.phyton" }, - { ".qcp", "audio/vnd.qcelp" }, - { ".qif", "image/x-quicktime" }, - { ".qtc", "video/x-qtc" }, - { ".qtif", "image/x-quicktime" }, - { ".qti", "image/x-quicktime" }, - { ".qt", "video/quicktime" }, - { ".ra", "audio/x-pn-realaudio" }, - { ".ram", "audio/x-pn-realaudio" }, - { ".ras", "application/x-cmu-raster" }, - { ".rast", "image/cmu-raster" }, - { ".rexx", "text/x-script.rexx" }, - { ".rf", "image/vnd.rn-realflash" }, - { ".rgb", "image/x-rgb" }, - { ".rm", "application/vnd.rn-realmedia" }, - { ".rmi", "audio/mid" }, - { ".rmm", "audio/x-pn-realaudio" }, - { ".rmp", "audio/x-pn-realaudio" }, - { ".rng", "application/ringing-tones" }, - { ".rnx", "application/vnd.rn-realplayer" }, - { ".roff", "application/x-troff" }, - { ".rp", "image/vnd.rn-realpix" }, - { ".rpm", "audio/x-pn-realaudio-plugin" }, - { ".rss", "application/rss+xml" }, - { ".rtf", "text/rtf" }, - { ".rt", "text/richtext" }, - { ".rtx", "text/richtext" }, - { ".rv", "video/vnd.rn-realvideo" }, - { ".s3m", "audio/s3m" }, - { ".sbk", "application/x-tbook" }, - { ".scd", "application/x-msschedule" }, - { ".scm", "application/x-lotusscreencam" }, - { ".sct", "text/scriptlet" }, - { ".sdml", "text/plain" }, - { ".sdp", "application/sdp" }, - { ".sdr", "application/sounder" }, - { ".sea", "application/sea" }, - { ".set", "application/set" }, - { ".setpay", "application/set-payment-initiation" }, - { ".setreg", "application/set-registration-initiation" }, - { ".sgml", "text/sgml" }, - { ".sgm", "text/sgml" }, - { ".shar", "application/x-bsh" }, - { ".sh", "text/x-script.sh" }, - { ".shtml", "text/html" }, - { ".sid", "audio/x-psid" }, - { ".silo", "model/mesh" }, - { ".sit", "application/x-sit" }, - { ".skd", "application/x-koan" }, - { ".skm", "application/x-koan" }, - { ".skp", "application/x-koan" }, - { ".skt", "application/x-koan" }, - { ".sl", "application/x-seelogo" }, - { ".smi", "application/smil" }, - { ".smil", "application/smil" }, - { ".snd", "audio/basic" }, - { ".sol", "application/solids" }, - { ".spc", "application/x-pkcs7-certificates" }, - { ".spl", "application/futuresplash" }, - { ".spr", "application/x-sprite" }, - { ".sprite", "application/x-sprite" }, - { ".spx", "audio/ogg" }, - { ".src", "application/x-wais-source" }, - { ".ssi", "text/x-server-parsed-html" }, - { ".ssm", "application/streamingmedia" }, - { ".sst", "application/vnd.ms-pki.certstore" }, - { ".step", "application/step" }, - { ".s", "text/x-asm" }, - { ".stl", "application/sla" }, - { ".stm", "text/html" }, - { ".stp", "application/step" }, - { ".sv4cpio", "application/x-sv4cpio" }, - { ".sv4crc", "application/x-sv4crc" }, - { ".svf", "image/x-dwg" }, - { ".svg", "image/svg+xml" }, - { ".svr", "application/x-world" }, - { ".swf", "application/x-shockwave-flash" }, - { ".talk", "text/x-speech" }, - { ".t", "application/x-troff" }, - { ".tar", "application/x-tar" }, - { ".tbk", "application/toolbook" }, - { ".tcl", "text/x-script.tcl" }, - { ".tcsh", "text/x-script.tcsh" }, - { ".tex", "application/x-tex" }, - { ".texi", "application/x-texinfo" }, - { ".texinfo", "application/x-texinfo" }, - { ".text", "text/plain" }, - { ".tgz", "application/x-compressed" }, - { ".tiff", "image/tiff" }, - { ".tif", "image/tiff" }, - { ".tr", "application/x-troff" }, - { ".trm", "application/x-msterminal" }, - { ".ts", "application/typescript" }, - { ".tsi", "audio/tsp-audio" }, - { ".tsp", "audio/tsplayer" }, - { ".tsv", "text/tab-separated-values" }, - { ".ttc", "font/collection" }, - { ".ttf", "font/ttf" }, - { ".txt", "text/plain" }, - { ".uil", "text/x-uil" }, - { ".uls", "text/iuls" }, - { ".unis", "text/uri-list" }, - { ".uni", "text/uri-list" }, - { ".unv", "application/i-deas" }, - { ".uris", "text/uri-list" }, - { ".uri", "text/uri-list" }, - { ".ustar", "multipart/x-ustar" }, - { ".uue", "text/x-uuencode" }, - { ".uu", "text/x-uuencode" }, - { ".vcd", "application/x-cdlink" }, - { ".vcf", "text/vcard" }, - { ".vcard", "text/vcard" }, - { ".vcs", "text/x-vcalendar" }, - { ".vda", "application/vda" }, - { ".vdo", "video/vdo" }, - { ".vew", "application/groupwise" }, - { ".vivo", "video/vnd.vivo" }, - { ".viv", "video/vnd.vivo" }, - { ".vmd", "application/vocaltec-media-desc" }, - { ".vmf", "application/vocaltec-media-file" }, - { ".voc", "audio/voc" }, - { ".vos", "video/vosaic" }, - { ".vox", "audio/voxware" }, - { ".vqe", "audio/x-twinvq-plugin" }, - { ".vqf", "audio/x-twinvq" }, - { ".vql", "audio/x-twinvq-plugin" }, - { ".vrml", "application/x-vrml" }, - { ".vsd", "application/x-visio" }, - { ".vst", "application/x-visio" }, - { ".vsw", "application/x-visio" }, - { ".w60", "application/wordperfect6.0" }, - { ".w61", "application/wordperfect6.1" }, - { ".w6w", "application/msword" }, - { ".wav", "audio/wav" }, - { ".wb1", "application/x-qpro" }, - { ".wbmp", "image/vnd.wap.wbmp" }, - { ".wcm", "application/vnd.ms-works" }, - { ".wdb", "application/vnd.ms-works" }, - { ".web", "application/vnd.xara" }, - { ".weba", "audio/webm" }, - { ".webm", "video/webm" }, - { ".webp", "image/webp" }, - { ".wiz", "application/msword" }, - { ".wk1", "application/x-123" }, - { ".wks", "application/vnd.ms-works" }, - { ".wmf", "image/wmf" }, - { ".wmlc", "application/vnd.wap.wmlc" }, - { ".wmlsc", "application/vnd.wap.wmlscriptc" }, - { ".wmls", "text/vnd.wap.wmlscript" }, - { ".wml", "text/vnd.wap.wml" }, - { ".wmp", "video/x-ms-wmp" }, - { ".wmv", "video/x-ms-wmv" }, - { ".wmx", "video/x-ms-wmx" }, - { ".woff", "font/woff" }, - { ".woff2", "font/woff2" }, - { ".word", "application/msword" }, - { ".wp5", "application/wordperfect" }, - { ".wp6", "application/wordperfect" }, - { ".wp", "application/wordperfect" }, - { ".wpd", "application/wordperfect" }, - { ".wps", "application/vnd.ms-works" }, - { ".wq1", "application/x-lotus" }, - { ".wri", "application/mswrite" }, - { ".wrl", "application/x-world" }, - { ".wrz", "model/vrml" }, - { ".wsc", "text/scriplet" }, - { ".wsdl", "text/xml" }, - { ".wsrc", "application/x-wais-source" }, - { ".wtk", "application/x-wintalk" }, - { ".wvx", "video/x-ms-wvx" }, - { ".x3d", "model/x3d+xml" }, - { ".x3db", "model/x3d+fastinfoset" }, - { ".x3dv", "model/x3d-vrml" }, - { ".xaml", "application/xaml+xml" }, - { ".xap", "application/x-silverlight-app" }, - { ".xbap", "application/x-ms-xbap" }, - { ".xbm", "image/x-xbitmap" }, - { ".xdr", "video/x-amt-demorun" }, - { ".xht", "application/xhtml+xml" }, - { ".xhtml", "application/xhtml+xml" }, - { ".xif", "image/vnd.xiff" }, - { ".xla", "application/vnd.ms-excel" }, - { ".xlam", "application/vnd.ms-excel.addin.macroEnabled.12" }, - { ".xl", "application/excel" }, - { ".xlb", "application/excel" }, - { ".xlc", "application/excel" }, - { ".xld", "application/excel" }, - { ".xlk", "application/excel" }, - { ".xll", "application/excel" }, - { ".xlm", "application/excel" }, - { ".xls", "application/vnd.ms-excel" }, - { ".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12" }, - { ".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12" }, - { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, - { ".xlt", "application/vnd.ms-excel" }, - { ".xltm", "application/vnd.ms-excel.template.macroEnabled.12" }, - { ".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" }, - { ".xlv", "application/excel" }, - { ".xlw", "application/excel" }, - { ".xm", "audio/xm" }, - { ".xml", "text/xml" }, - { ".xpi", "application/x-xpinstall" }, - { ".xpix", "application/x-vnd.ls-xpix" }, - { ".xpm", "image/xpm" }, - { ".xps", "application/vnd.ms-xpsdocument" }, - { ".x-png", "image/png" }, - { ".xsd", "text/xml" }, - { ".xsl", "text/xml" }, - { ".xslt", "text/xml" }, - { ".xsr", "video/x-amt-showrun" }, - { ".xwd", "image/x-xwd" }, - { ".z", "application/x-compressed" }, - { ".zip", "application/zip" }, - { ".zsh", "text/x-script.zsh" } - }; - } - - static Dictionary LoadExtensions () - { - return new Dictionary (MimeUtils.OrdinalIgnoreCase) { - { "application/acad", ".dwg" }, - { "application/atom+xml", ".atom" }, - { "application/base64", ".mm" }, - { "application/binhex", ".hqx" }, - { "application/book", ".boo" }, - { "application/cdf", ".cdf" }, - { "application/clariscad", ".ccad" }, - { "application/commonground", ".dp" }, - { "application/dart", ".dart" }, - { "application/drafting", ".drw" }, - { "application/dxf", ".dxf" }, - { "application/envoy", ".evy" }, - { "application/epub+zip", ".epub" }, - { "application/excel", ".xls" }, - { "application/freeloader", ".frl" }, - { "application/fsharp-script", ".fsx" }, - { "application/futuresplash", ".spl" }, - { "application/groupwise", ".vew" }, - { "application/hta", ".hta" }, - { "application/i-deas", ".unv" }, - { "application/inf", ".inf" }, - { "application/internet-property-stream", ".acx" }, - { "application/java-archive", ".jar" }, - { "application/javascript", ".js" }, - { "application/json", ".json" }, - { "application/marc", ".mrc" }, - { "application/mbedlet", ".mbd" }, - { "application/mcad", ".mcd" }, - { "application/mime", ".aps" }, - { "application/mspowerpoint", ".ppz" }, - { "application/msword", ".doc" }, - { "application/mswrite", ".wri" }, - { "application/netmc", ".mcp" }, - { "application/octet-stream", ".bin" }, - { "application/oda", ".oda" }, - { "application/ogg", ".ogx" }, - { "application/oleobject", ".ods" }, - { "application/olescript", ".axs" }, - { "application/oxps", ".oxps" }, - { "application/pdf", ".pdf" }, - { "application/pics-rules", ".prf" }, - { "application/pkcs-12", ".p12" }, - { "application/pkcs10", ".p10" }, - { "application/pkcs7-mime", ".p7m" }, - { "application/pkcs7-signature", ".p7s" }, - { "application/pkix-cert", ".cer" }, - { "application/pkix-crl", ".crl" }, - { "application/postscript", ".ps" }, - { "application/pro_eng", ".part" }, - { "application/ringing-tones", ".rng" }, - { "application/rss+xml", ".rss" }, - { "application/sdp", ".sdp" }, - { "application/sea", ".sea" }, - { "application/set", ".set" }, - { "application/set-payment-initiation", ".setpay" }, - { "application/set-registration-initiation", ".setreg" }, - { "application/sla", ".stl" }, - { "application/smil", ".smi" }, - { "application/solids", ".sol" }, - { "application/sounder", ".sdr" }, - { "application/step", ".step" }, - { "application/streamingmedia", ".ssm" }, - { "application/toolbook", ".tbk" }, - { "application/typescript", ".ts" }, - { "application/vda", ".vda" }, - { "application/vnd.bw-fontobject", ".eot" }, - { "application/vnd.fdf", ".fdf" }, - { "application/vnd.hp-HPGL", ".hgl" }, - { "application/vnd.ms-excel", ".xls" }, - { "application/vnd.ms-excel.addin.macroEnabled.12", ".xlam" }, - { "application/vnd.ms-excel.sheet.binary.macroEnabled.12", ".xlsb" }, - { "application/vnd.ms-excel.sheet.macroEnabled.12", ".xlsm" }, - { "application/vnd.ms-excel.template.macroEnabled.12", ".xltm" }, - { "application/vnd.ms-htmlhelp", ".chm" }, - { "application/vnd.ms-pki.certstore", ".sst" }, - { "application/vnd.ms-pki.pko", ".pko" }, - { "application/vnd.ms-pki.seccat", ".cat" }, - { "application/vnd.ms-powerpoint", ".ppt" }, - { "application/vnd.ms-powerpoint.addin.macroEnabled.12", ".ppam" }, - { "application/vnd.ms-powerpoint.presentation.macroEnabled.12", ".pptm" }, - { "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", ".ppsm" }, - { "application/vnd.ms-powerpoint.template.macroEnabled.12", ".potm" }, - { "application/vnd.ms-project", ".mpp" }, - { "application/vnd.ms-word.document.macroEnabled.12", ".docm" }, - { "application/vnd.ms-word.template.macroEnabled.12", ".dotm" }, - { "application/vnd.ms-works", ".wcm" }, - { "application/vnd.ms-xpsdocument", ".xps" }, - { "application/vnd.nokia.configuration-message", ".ncm" }, - { "application/vnd.openxmlformats-officedocument.presentationml.presentation", ".pptx" }, - { "application/vnd.openxmlformats-officedocument.presentationml.slideshow", ".ppsx" }, - { "application/vnd.openxmlformats-officedocument.presentationml.template", ".potx" }, - { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xlsx" }, - { "application/vnd.openxmlformats-officedocument.spreadsheetml.template", ".xltx" }, - { "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".docx" }, - { "application/vnd.openxmlformats-officedocument.wordprocessingml.template", ".dotx" }, - { "application/vnd.rn-realmedia", ".rm" }, - { "application/vnd.rn-realplayer", ".rnx" }, - { "application/vnd.wap.wmlc", ".wmlc" }, - { "application/vnd.wap.wmlscriptc", ".wmlsc" }, - { "application/vnd.xara", ".web" }, - { "application/vocaltec-media-desc", ".vmd" }, - { "application/vocaltec-media-file", ".vmf" }, - { "application/wordperfect", ".wp5" }, - { "application/wordperfect6.0", ".w60" }, - { "application/wordperfect6.1", ".w61" }, - { "application/x-123", ".wk1" }, - { "application/x-7z-compressed", ".7z" }, - { "application/x-aim", ".aim" }, - { "application/x-authorware-bin", ".aab" }, - { "application/x-authorware-map", ".aam" }, - { "application/x-authorware-seg", ".aas" }, - { "application/x-bcpio", ".bcpio" }, - { "application/x-bsh", ".bsh" }, - { "application/x-bytecode.python", ".pyc" }, - { "application/x-bzip", ".bz" }, - { "application/x-bzip2", ".bz2" }, - { "application/x-cdlink", ".vcd" }, - { "application/x-chat", ".cha" }, - { "application/x-cmu-raster", ".ras" }, - { "application/x-cocoa", ".cco" }, - { "application/x-compressed", ".z" }, - { "application/x-conference", ".nsc" }, - { "application/x-cpio", ".cpio" }, - { "application/x-cpt", ".cpt" }, - { "application/x-csh", ".csh" }, - { "application/x-deb", ".deb" }, - { "application/x-deepv", ".deepv" }, - { "application/x-director", ".dcr" }, - { "application/x-dvi", ".dvi" }, - { "application/x-elc", ".elc" }, - { "application/x-esrehber", ".es" }, - { "application/x-font-ttf", ".ttf" }, - { "application/x-freelance", ".pre" }, - { "application/x-gsp", ".gsp" }, - { "application/x-gss", ".gss" }, - { "application/x-gtar", ".gtar" }, - { "application/x-gzip", ".gz" }, - { "application/x-hdf", ".hdf" }, - { "application/x-helpfile", ".help" }, - { "application/x-httpd-imap", ".imap" }, - { "application/x-ima", ".ima" }, - { "application/x-internet-signup", ".isp" }, - { "application/x-internett-signup", ".ins" }, - { "application/x-inventor", ".iv" }, - { "application/x-ip2", ".ip" }, - { "application/x-iphone", ".iii" }, - { "application/x-java-applet", ".class" }, - { "application/x-java-commerce", ".jcm" }, - { "application/x-koan", ".skd" }, - { "application/x-latex", ".latex" }, - { "application/x-lisp", ".lsp" }, - { "application/x-livescreen", ".ivy" }, - { "application/x-lotus", ".wq1" }, - { "application/x-lotusscreencam", ".scm" }, - { "application/x-magic-cap-package-1.0", ".mc$" }, - { "application/x-mif", ".mif" }, - { "application/x-mix-transfer", ".nix" }, - { "application/x-mplayer2", ".asx" }, - { "application/x-ms-application", ".application" }, - { "application/x-ms-manifest", ".manifest" }, - { "application/x-ms-xbap", ".xbap" }, - { "application/x-msaccess", ".mdb" }, - { "application/x-mscardfile", ".crd" }, - { "application/x-msclip", ".clp" }, - { "application/x-msdownload", ".dll" }, - { "application/x-msmediaview", ".m13" }, - { "application/x-msmoney", ".mny" }, - { "application/x-mspublisher", ".pub" }, - { "application/x-msschedule", ".scd" }, - { "application/x-msterminal", ".trm" }, - { "application/x-navi-animation", ".ani" }, - { "application/x-navidoc", ".nvd" }, - { "application/x-navimap", ".map" }, - { "application/x-netcdf", ".nc" }, - { "application/x-newton-compatible-pkg", ".pkg" }, - { "application/x-nokia-9000-communicator-add-on-software", ".aos" }, - { "application/x-omc", ".omc" }, - { "application/x-omcdatamaker", ".omcd" }, - { "application/x-omcregerator", ".omcr" }, - { "application/x-pagemaker", ".pm4" }, - { "application/x-pcl", ".pcl" }, - { "application/x-perfmon", ".pma" }, - { "application/x-PiXCLscript", ".plx" }, - { "application/x-pkcs12", ".pfx" }, - { "application/x-pkcs7-certificates", ".p7b" }, - { "application/x-pkcs7-certreqresp", ".p7r" }, - { "application/x-pkcs7-signature", ".p7a" }, - { "application/x-portable-anymap", ".pnm" }, - { "application/x-project", ".mpc" }, - { "application/x-qpro", ".wb1" }, - { "application/x-seelogo", ".sl" }, - { "application/x-shockwave-flash", ".swf" }, - { "application/x-silverlight-app", ".xap" }, - { "application/x-sit", ".sit" }, - { "application/x-sprite", ".spr" }, - { "application/x-sv4cpio", ".sv4cpio" }, - { "application/x-sv4crc", ".sv4crc" }, - { "application/x-tar", ".tar" }, - { "application/x-tbook", ".sbk" }, - { "application/x-tex", ".tex" }, - { "application/x-texinfo", ".texi" }, - { "application/x-troff", ".roff" }, - { "application/x-troff-man", ".man" }, - { "application/x-troff-me", ".me" }, - { "application/x-troff-ms", ".ms" }, - { "application/x-visio", ".vsd" }, - { "application/x-vnd.AudioExplosion.mzz", ".mzz" }, - { "application/x-vnd.ls-xpix", ".xpix" }, - { "application/x-vrml", ".vrml" }, - { "application/x-wais-source", ".src" }, - { "application/x-wintalk", ".wtk" }, - { "application/x-woff", ".woff" }, - { "application/x-world", ".svr" }, - { "application/x-x509-ca-cert", ".der" }, - { "application/x-xpinstall", ".xpi" }, - { "application/xaml+xml", ".xaml" }, - { "application/xhtml+xml", ".xhtml" }, - { "application/xml", ".xml" }, - { "application/xml-dtd", ".dtd" }, - { "application/zip", ".zip" }, - { "audio/aac", ".aac" }, - { "audio/aiff", ".aiff" }, - { "audio/basic", ".snd" }, - { "audio/it", ".it" }, - { "audio/mid", ".rmi" }, - { "audio/midi", ".mid" }, - { "audio/mod", ".mod" }, - { "audio/mp4", ".mp4" }, - { "audio/mpeg", ".mp3" }, - { "audio/nspaudio", ".la" }, - { "audio/ogg", ".ogg" }, - { "audio/s3m", ".s3m" }, - { "audio/tsp-audio", ".tsi" }, - { "audio/tsplayer", ".tsp" }, - { "audio/vnd.qcelp", ".qcp" }, - { "audio/voc", ".voc" }, - { "audio/vorbis", ".ogg" }, - { "audio/voxware", ".vox" }, - { "audio/wav", ".wav" }, - { "audio/webm", ".weba" }, - { "audio/x-au", ".au" }, - { "audio/x-flac", ".flac" }, - { "audio/x-gsm", ".gsd" }, - { "audio/x-jam", ".jam" }, - { "audio/x-liveaudio", ".lam" }, - { "audio/x-mpequrl", ".m3u" }, - { "audio/x-pn-realaudio", ".ra" }, - { "audio/x-pn-realaudio-plugin", ".rpm" }, - { "audio/x-psid", ".sid" }, - { "audio/x-twinvq", ".vqf" }, - { "audio/x-twinvq-plugin", ".vqe" }, - { "audio/x-vnd.AudioExplosion.MjuiceMediaFile", ".mjf" }, - { "audio/xm", ".xm" }, - { "font/collection", ".ttc" }, - { "font/otf", ".otf" }, - { "font/sfnt", ".ttf" }, - { "font/ttf", ".ttf" }, - { "font/woff", ".woff" }, - { "font/woff2", ".woff2" }, - { "image/bmp", ".bmp" }, - { "image/cis-cod", ".cod" }, - { "image/cmu-raster", ".rast" }, - { "image/fif", ".fif" }, - { "image/g3fax", ".g3" }, - { "image/gif", ".gif" }, - { "image/ief", ".ief" }, - { "image/jpeg", ".jpg" }, - { "image/jutvision", ".jut" }, - { "image/naplps", ".nap" }, - { "image/pict", ".pic" }, - { "image/png", ".png" }, - { "image/svg+xml", ".svg" }, - { "image/tiff", ".tif" }, - { "image/vasa", ".mcf" }, - { "image/vnd.fpx", ".fpx" }, - { "image/vnd.rn-realflash", ".rf" }, - { "image/vnd.rn-realpix", ".rp" }, - { "image/vnd.wap.wbmp", ".wbmp" }, - { "image/vnd.xiff", ".xif" }, - { "image/webp", ".webp" }, - { "image/wmf", ".wmf" }, - { "image/x-cmx", ".cmx" }, - { "image/x-dwg", ".svf" }, - { "image/x-icon", ".ico" }, - { "image/x-jg", ".art" }, - { "image/x-jps", ".jps" }, - { "image/x-niff", ".niff" }, - { "image/x-pcx", ".pcx" }, - { "image/x-pict", ".pct" }, - { "image/x-png", ".png" }, - { "image/x-portable-bitmap", ".pbm" }, - { "image/x-portable-graymap", ".pgm" }, - { "image/x-portable-pixmap", ".ppm" }, - { "image/x-quicktime", ".qif" }, - { "image/x-rgb", ".rgb" }, - { "image/x-xbitmap", ".xbm" }, - { "image/x-xpixmap", ".pm" }, - { "image/x-xwd", ".xwd" }, - { "image/xpm", ".xpm" }, - { "message/rfc822", ".eml" }, - { "model/iges", ".iges" }, - { "model/mesh", ".mesh" }, - { "model/vrml", ".wrz" }, - { "model/x-pov", ".pov" }, - { "model/x3d+fastinfoset", ".x3db" }, - { "model/x3d+xml", ".x3d" }, - { "model/x3d-vrml", ".x3dv" }, - { "multipart/x-ustar", ".ustar" }, - { "text/asp", ".asp" }, - { "text/cache-manifest", ".appcache" }, - { "text/calendar", ".ics" }, - { "text/css", ".css" }, - { "text/csv", ".csv" }, - { "text/h323", ".323" }, - { "text/html", ".html" }, - { "text/iuls", ".uls" }, - { "text/markdown", ".md" }, - { "text/pascal", ".pas" }, - { "text/plain", ".txt" }, - { "text/richtext", ".rtx" }, - { "text/rtf", ".rtf" }, - { "text/scriplet", ".wsc" }, - { "text/scriptlet", ".sct" }, - { "text/sgml", ".sgml" }, - { "text/tab-separated-values", ".tsv" }, - { "text/uri-list", ".uri" }, - { "text/vcard", ".vcf" }, - { "text/vnd.abc", ".abc" }, - { "text/vnd.fmi.flexstor", ".flx" }, - { "text/vnd.wap.wml", ".wml" }, - { "text/vnd.wap.wmlscript", ".wmls" }, - { "text/webviewhtml", ".htt" }, - { "text/x-asm", ".asm" }, - { "text/x-audiosoft-intra", ".aip" }, - { "text/x-coffeescript", ".coffee" }, - { "text/x-component", ".htc" }, - { "text/x-la-asf", ".lsx" }, - { "text/x-pascal", ".p" }, - { "text/x-script", ".hlb" }, - { "text/x-script.elisp", ".el" }, - { "text/x-script.ksh", ".ksh" }, - { "text/x-script.phyton", ".py" }, - { "text/x-script.rexx", ".rexx" }, - { "text/x-script.sh", ".sh" }, - { "text/x-script.tcl", ".tcl" }, - { "text/x-script.tcsh", ".tcsh" }, - { "text/x-script.zsh", ".zsh" }, - { "text/x-server-parsed-html", ".ssi" }, - { "text/x-setext", ".etx" }, - { "text/x-speech", ".talk" }, - { "text/x-uil", ".uil" }, - { "text/x-uuencode", ".uu" }, - { "text/x-vcalendar", ".vcs" }, - { "text/xml", ".xml" }, - { "video/3gpp", ".3gp" }, - { "video/3gpp2", ".3g2" }, - { "video/animaflex", ".afl" }, - { "video/avi", ".avi" }, - { "video/avs-video", ".avs" }, - { "video/dl", ".dl" }, - { "video/fli", ".fli" }, - { "video/gl", ".gl" }, - { "video/mp4", ".mp4" }, - { "video/mpeg", ".mpg" }, - { "video/ogg", ".ogv" }, - { "video/quicktime", ".mov" }, - { "video/vdo", ".vdo" }, - { "video/vnd.rn-realvideo", ".rv" }, - { "video/vnd.vivo", ".vivo" }, - { "video/vosaic", ".vos" }, - { "video/webm", ".webm" }, - { "video/x-amt-demorun", ".xdr" }, - { "video/x-amt-showrun", ".xsr" }, - { "video/x-atomic3d-feature", ".fmf" }, - { "video/x-dv", ".dif" }, - { "video/x-isvideo", ".isu" }, - { "video/x-ivf", ".ivf" }, - { "video/x-motion-jpeg", ".mjpg" }, - { "video/x-ms-asf", ".asf" }, - { "video/x-ms-wmp", ".wmp" }, - { "video/x-ms-wmv", ".wmv" }, - { "video/x-ms-wmx", ".wmx" }, - { "video/x-ms-wvx", ".wvx" }, - { "video/x-qtc", ".qtc" }, - { "video/x-sgi-movie", ".movie" } - }; - } - - /// - /// Get the MIME-type of a file. - /// - /// - /// Gets the MIME-type of a file based on the file extension. - /// - /// The MIME-type. - /// The file name. - /// - /// is null. - /// - public static string GetMimeType (string fileName) - { - if (fileName == null) - throw new ArgumentNullException (nameof (fileName)); - - var extension = Path.GetExtension (fileName); - - mimeTypes.TryGetValue (extension, out var mimeType); - - return mimeType ?? "application/octet-stream"; - } - - /// - /// Get the standard file extension for a MIME-type. - /// - /// - /// Gets the standard file extension for a MIME-type. - /// - /// true if the extension is known for the specified MIME-type; otherwise, false. - /// The MIME-type. - /// The file name extension for the specified MIME-type. - /// - /// is null. - /// - public static bool TryGetExtension (string mimeType, out string extension) - { - if (mimeType == null) - throw new ArgumentNullException (nameof (mimeType)); - - return extensions.TryGetValue (mimeType, out extension); - } - - /// - /// Register a MIME-type to file extension mapping. - /// - /// - /// Registers a MIME-type to file extension mapping. - /// If the mapping for the MIME-type and/or file extension already exists, - /// then it is overridden by the new mapping. - /// - /// The MIME-type to register. - /// The file extension to register. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is empty. - /// -or- - /// is empty. - /// - public static void Register (string mimeType, string extension) - { - if (mimeType == null) - throw new ArgumentNullException (nameof (mimeType)); - - if (mimeType.Length == 0) - throw new ArgumentException ("Cannot register an empty MIME-type.", nameof (mimeType)); - - if (extension == null) - throw new ArgumentNullException (nameof (extension)); - - if (extension.Length == 0) - throw new ArgumentException ("Cannot register an empty file extension.", nameof (extension)); - - if (extension[0] != '.') - extension = "." + extension; - - mimeTypes[extension] = mimeType; - extensions[mimeType] = extension; - } - } -} diff --git a/src/MimeKit/MimeVisitor.cs b/src/MimeKit/MimeVisitor.cs deleted file mode 100644 index 39b2cad..0000000 --- a/src/MimeKit/MimeVisitor.cs +++ /dev/null @@ -1,385 +0,0 @@ -// -// MimeVisitor.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -#if ENABLE_CRYPTO -using MimeKit.Cryptography; -#endif - -using MimeKit.Tnef; - -namespace MimeKit { - /// - /// Represents a visitor for MIME trees. - /// - /// - /// This class is designed to be inherited to create more specialized classes whose - /// functionality requires traversing, examining or copying a MIME tree. - /// - /// - /// - /// - public abstract class MimeVisitor - { - /// - /// Dispatches the entity to one of the more specialized visit methods in this class. - /// - /// - /// Dispatches the entity to one of the more specialized visit methods in this class. - /// - /// The MIME entity. - public virtual void Visit (MimeEntity entity) - { - if (entity != null) - entity.Accept (this); - } - - /// - /// Dispatches the message to one of the more specialized visit methods in this class. - /// - /// - /// Dispatches the message to one of the more specialized visit methods in this class. - /// - /// The MIME message. - public virtual void Visit (MimeMessage message) - { - if (message != null) - message.Accept (this); - } - -#if ENABLE_CRYPTO - /// - /// Visit the application/pgp-encrypted MIME entity. - /// - /// - /// Visits the application/pgp-encrypted MIME entity. - /// - /// - /// The application/pgp-encrypted MIME entity. - protected internal virtual void VisitApplicationPgpEncrypted (ApplicationPgpEncrypted entity) - { - VisitMimePart (entity); - } - - /// - /// Visit the application/pgp-signature MIME entity. - /// - /// - /// Visits the application/pgp-signature MIME entity. - /// - /// - /// The application/pgp-signature MIME entity. - protected internal virtual void VisitApplicationPgpSignature (ApplicationPgpSignature entity) - { - VisitMimePart (entity); - } - - /// - /// Visit the application/pkcs7-mime MIME entity. - /// - /// - /// Visits the application/pkcs7-mime MIME entity. - /// - /// The application/pkcs7-mime MIME entity. - protected internal virtual void VisitApplicationPkcs7Mime (ApplicationPkcs7Mime entity) - { - VisitMimePart (entity); - } - - /// - /// Visit the application/pkcs7-signature MIME entity. - /// - /// - /// Visits the application/pkcs7-signature MIME entity. - /// - /// - /// The application/pkcs7-signature MIME entity. - protected internal virtual void VisitApplicationPkcs7Signature (ApplicationPkcs7Signature entity) - { - VisitMimePart (entity); - } -#endif - - /// - /// Visit the message/disposition-notification MIME entity. - /// - /// - /// Visits the message/disposition-notification MIME entity. - /// - /// The message/disposition-notification MIME entity. - protected internal virtual void VisitMessageDispositionNotification (MessageDispositionNotification entity) - { - VisitMimePart (entity); - } - - /// - /// Visit the message/delivery-status MIME entity. - /// - /// - /// Visits the message/delivery-status MIME entity. - /// - /// The message/delivery-status MIME entity. - protected internal virtual void VisitMessageDeliveryStatus (MessageDeliveryStatus entity) - { - VisitMimePart (entity); - } - - /// - /// Visit the message contained within a message/rfc822 or message/news MIME entity. - /// - /// - /// Visits the message contained within a message/rfc822 or message/news MIME entity. - /// - /// The message/rfc822 or message/news MIME entity. - protected virtual void VisitMessage (MessagePart entity) - { - if (entity.Message != null) - entity.Message.Accept (this); - } - - /// - /// Visit the message/rfc822 or message/news MIME entity. - /// - /// - /// Visits the message/rfc822 or message/news MIME entity. - /// - /// - /// - /// - /// The message/rfc822 or message/news MIME entity. - protected internal virtual void VisitMessagePart (MessagePart entity) - { - VisitMimeEntity (entity); - VisitMessage (entity); - } - - /// - /// Visit the message/partial MIME entity. - /// - /// - /// Visits the message/partial MIME entity. - /// - /// The message/partial MIME entity. - protected internal virtual void VisitMessagePartial (MessagePartial entity) - { - VisitMimePart (entity); - } - - /// - /// Visit the abstract MIME entity. - /// - /// - /// Visits the abstract MIME entity. - /// - /// The MIME entity. - protected internal virtual void VisitMimeEntity (MimeEntity entity) - { - } - - /// - /// Visit the body of the message. - /// - /// - /// Visits the body of the message. - /// - /// The message. - protected virtual void VisitBody (MimeMessage message) - { - if (message.Body != null) - message.Body.Accept (this); - } - - /// - /// Visit the MIME message. - /// - /// - /// Visits the MIME message. - /// - /// The MIME message. - protected internal virtual void VisitMimeMessage (MimeMessage message) - { - VisitBody (message); - } - - /// - /// Visit the abstract MIME part entity. - /// - /// - /// Visits the MIME part entity. - /// - /// - /// - /// - /// The MIME part entity. - protected internal virtual void VisitMimePart (MimePart entity) - { - VisitMimeEntity (entity); - } - - /// - /// Visit the children of a . - /// - /// - /// Visits the children of a . - /// - /// Multipart. - protected virtual void VisitChildren (Multipart multipart) - { - for (int i = 0; i < multipart.Count; i++) - multipart[i].Accept (this); - } - - /// - /// Visit the abstract multipart MIME entity. - /// - /// - /// Visits the abstract multipart MIME entity. - /// - /// The multipart MIME entity. - protected internal virtual void VisitMultipart (Multipart multipart) - { - VisitMimeEntity (multipart); - VisitChildren (multipart); - } - - /// - /// Visit the multipart/alternative MIME entity. - /// - /// - /// Visits the multipart/alternative MIME entity. - /// - /// - /// - /// - /// The multipart/alternative MIME entity. - protected internal virtual void VisitMultipartAlternative (MultipartAlternative alternative) - { - VisitMultipart (alternative); - } - -#if ENABLE_CRYPTO - /// - /// Visit the multipart/encrypted MIME entity. - /// - /// - /// Visits the multipart/encrypted MIME entity. - /// - /// The multipart/encrypted MIME entity. - protected internal virtual void VisitMultipartEncrypted (MultipartEncrypted encrypted) - { - VisitMultipart (encrypted); - } -#endif - - /// - /// Visit the multipart/related MIME entity. - /// - /// - /// Visits the multipart/related MIME entity. - /// - /// - /// - /// - /// The multipart/related MIME entity. - protected internal virtual void VisitMultipartRelated (MultipartRelated related) - { - VisitMultipart (related); - } - - /// - /// Visit the multipart/report MIME entity. - /// - /// - /// Visits the multipart/report MIME entity. - /// - /// - /// - /// - /// The multipart/report MIME entity. - protected internal virtual void VisitMultipartReport (MultipartReport report) - { - VisitMultipart (report); - } - -#if ENABLE_CRYPTO - /// - /// Visit the multipart/signed MIME entity. - /// - /// - /// Visits the multipart/signed MIME entity. - /// - /// The multipart/signed MIME entity. - protected internal virtual void VisitMultipartSigned (MultipartSigned signed) - { - VisitMultipart (signed); - } -#endif - - /// - /// Visit the text-based MIME part entity. - /// - /// - /// Visits the text-based MIME part entity. - /// - /// - /// - /// - /// The text-based MIME part entity. - protected internal virtual void VisitTextPart (TextPart entity) - { - VisitMimePart (entity); - } - - /// - /// Visit the text/rfc822-headers MIME entity. - /// - /// - /// Visits the text/rfc822-headers MIME entity. - /// - /// - /// - /// - /// The text/rfc822-headers MIME entity. - protected internal virtual void VisitTextRfc822Headers (TextRfc822Headers entity) - { - VisitMessagePart (entity); - } - - /// - /// Visit the Microsoft TNEF MIME part entity. - /// - /// - /// Visits the Microsoft TNEF MIME part entity. - /// - /// - /// - /// - /// The Microsoft TNEF MIME part entity. - protected internal virtual void VisitTnefPart (TnefPart entity) - { - VisitMimePart (entity); - } - } -} diff --git a/src/MimeKit/Multipart.cs b/src/MimeKit/Multipart.cs deleted file mode 100644 index 976f1ed..0000000 --- a/src/MimeKit/Multipart.cs +++ /dev/null @@ -1,828 +0,0 @@ -// -// Multipart.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Collections; -using System.Threading.Tasks; -using System.Collections.Generic; - -using MimeKit.Encodings; -using MimeKit.Utils; -using MimeKit.IO; - -namespace MimeKit { - /// - /// A multipart MIME entity which may contain a collection of MIME entities. - /// - /// - /// All multipart MIME entities will have a Content-Type with a media type of "multipart". - /// The most common multipart MIME entity used in email is the "multipart/mixed" entity. - /// Four (4) initial subtypes were defined in the original MIME specifications: mixed, alternative, - /// digest, and parallel. - /// The "multipart/mixed" type is a sort of general-purpose container. When used in email, the - /// first entity is typically the "body" of the message while additional entities are most often - /// file attachments. - /// Speaking of message "bodies", the "multipart/alternative" type is used to offer a list of - /// alternative formats for the main body of the message (usually they will be "text/plain" and - /// "text/html"). These alternatives are in order of increasing faithfulness to the original document - /// (in other words, the last entity will be in a format that, when rendered, will most closely match - /// what the sending client's WYSISYG editor produced). - /// The "multipart/digest" type will typically contain a digest of MIME messages and is most - /// commonly used by mailing-list software. - /// The "multipart/parallel" type contains entities that are all meant to be shown (or heard) - /// in parallel. - /// Another commonly used type is the "multipart/related" type which contains, as one might expect, - /// inter-related MIME parts which typically reference each other via URIs based on the Content-Id and/or - /// Content-Location headers. - /// - public class Multipart : MimeEntity, ICollection, IList - { - readonly List children; - string preamble, epilogue; - - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public Multipart (MimeEntityConstructorArgs args) : base (args) - { - children = new List (); - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified subtype. - /// - /// The multipart media sub-type. - /// An array of initialization parameters: headers and MIME entities. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// contains one or more arguments of an unknown type. - /// - public Multipart (string subtype, params object[] args) : this (subtype) - { - if (args == null) - throw new ArgumentNullException (nameof (args)); - - foreach (object obj in args) { - if (obj == null || TryInit (obj)) - continue; - - if (obj is MimeEntity entity) { - Add (entity); - continue; - } - - throw new ArgumentException ("Unknown initialization parameter: " + obj.GetType ()); - } - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with the specified subtype. - /// - /// The multipart media sub-type. - /// - /// is null. - /// - public Multipart (string subtype) : base ("multipart", subtype) - { - ContentType.Boundary = GenerateBoundary (); - children = new List (); - WriteEndBoundary = true; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new with a ContentType of multipart/mixed. - /// - public Multipart () : this ("mixed") - { - } - - static string GenerateBoundary () - { - var base64 = new Base64Encoder (true); - var digest = new byte[16]; - var buf = new byte[24]; - int length; - - MimeUtils.GetRandomBytes (digest); - - length = base64.Flush (digest, 0, digest.Length, buf); - - return "=-" + Encoding.ASCII.GetString (buf, 0, length); - } - - /// - /// Get or set the boundary. - /// - /// - /// Gets or sets the boundary parameter on the Content-Type header. - /// - /// The boundary. - /// - /// is null. - /// - public string Boundary { - get { return ContentType.Boundary; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (Boundary == value) - return; - - ContentType.Boundary = value.Trim (); - } - } - - internal byte[] RawPreamble { - get; set; - } - - /// - /// Get or set the preamble. - /// - /// - /// A multipart preamble appears before the first child entity of the - /// multipart and is typically used only in the top-level multipart - /// of the message to specify that the message is in MIME format and - /// therefore requires a MIME compliant email application to render - /// it correctly. - /// - /// The preamble. - public string Preamble { - get { - if (preamble == null && RawPreamble != null) - preamble = CharsetUtils.ConvertToUnicode (Headers.Options, RawPreamble, 0, RawPreamble.Length); - - return preamble; - } - set { - if (Preamble == value) - return; - - if (value != null) { - var folded = FoldPreambleOrEpilogue (FormatOptions.Default, value, false); - RawPreamble = Encoding.UTF8.GetBytes (folded); - preamble = folded; - } else { - RawPreamble = null; - preamble = null; - } - - WriteEndBoundary = true; - } - } - - internal byte[] RawEpilogue { - get; set; - } - - /// - /// Get or set the epilogue. - /// - /// - /// A multipart epiloque is the text that appears after the closing boundary - /// of the multipart and is typically either empty or a single new line - /// character sequence. - /// - /// The epilogue. - public string Epilogue { - get { - if (epilogue == null && RawEpilogue != null) { - int index = 0; - - // Note: In practice, the RawEpilogue contains the CRLF belonging to the end-boundary, but - // for sanity, we pretend that it doesn't. - if ((RawEpilogue.Length > 1 && RawEpilogue[0] == (byte) '\r' && RawEpilogue[1] == (byte) '\n')) - index += 2; - else if (RawEpilogue.Length > 1 && RawEpilogue[0] == (byte) '\n') - index++; - - epilogue = CharsetUtils.ConvertToUnicode (Headers.Options, RawEpilogue, index, RawEpilogue.Length - index); - } - - return epilogue; - } - set { - if (Epilogue == value) - return; - - if (value != null) { - var folded = FoldPreambleOrEpilogue (FormatOptions.Default, value, true); - RawEpilogue = Encoding.UTF8.GetBytes (folded); - epilogue = null; - } else { - RawEpilogue = null; - epilogue = null; - } - - WriteEndBoundary = true; - } - } - - /// - /// Get or set whether the end boundary should be written. - /// - /// - /// Gets or sets whether the end boundary should be written. - /// - /// true if the end boundary should be written; otherwise, false. - internal bool WriteEndBoundary { - get; set; - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMultipart (this); - } - - internal static string FoldPreambleOrEpilogue (FormatOptions options, string text, bool isEpilogue) - { - var builder = new StringBuilder (); - int startIndex, wordIndex; - int lineLength = 0; - int index = 0; - - if (isEpilogue) - builder.Append (options.NewLine); - - while (index < text.Length) { - startIndex = index; - - while (index < text.Length) { - if (!char.IsWhiteSpace (text[index])) - break; - - if (text[index] == '\n') { - builder.Append (options.NewLine); - startIndex = index + 1; - lineLength = 0; - } - - index++; - } - - wordIndex = index; - - while (index < text.Length && !char.IsWhiteSpace (text[index])) - index++; - - int length = index - startIndex; - - if (lineLength > 0 && lineLength + length >= options.MaxLineLength) { - builder.Append (options.NewLine); - length = index - wordIndex; - startIndex = wordIndex; - lineLength = 0; - } - - if (length > 0) { - builder.Append (text, startIndex, length); - lineLength += length; - } - } - - if (lineLength > 0) - builder.Append (options.NewLine); - - return builder.ToString (); - } - - static void WriteBytes (FormatOptions options, Stream stream, byte[] bytes, bool ensureNewLine, CancellationToken cancellationToken) - { - var cancellable = stream as ICancellableStream; - var filter = options.CreateNewLineFilter (ensureNewLine); - int index, length; - - var output = filter.Flush (bytes, 0, bytes.Length, out index, out length); - - if (cancellable != null) { - cancellable.Write (output, index, length, cancellationToken); - } else { - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (output, index, length); - } - } - - static Task WriteBytesAsync (FormatOptions options, Stream stream, byte[] bytes, bool ensureNewLine, CancellationToken cancellationToken) - { - var filter = options.CreateNewLineFilter (ensureNewLine); - int index, length; - - var output = filter.Flush (bytes, 0, bytes.Length, out index, out length); - - return stream.WriteAsync (output, index, length, cancellationToken); - } - - /// - /// Prepare the MIME entity for transport using the specified encoding constraints. - /// - /// - /// Prepares the MIME entity for transport using the specified encoding constraints. - /// - /// The encoding constraint. - /// The maximum number of octets allowed per line (not counting the CRLF). Must be between 60 and 998 (inclusive). - /// - /// is not between 60 and 998 (inclusive). - /// -or- - /// is not a valid value. - /// - public override void Prepare (EncodingConstraint constraint, int maxLineLength = 78) - { - if (maxLineLength < FormatOptions.MinimumLineLength || maxLineLength > FormatOptions.MaximumLineLength) - throw new ArgumentOutOfRangeException (nameof (maxLineLength)); - - for (int i = 0; i < children.Count; i++) - children[i].Prepare (constraint, maxLineLength); - } - - /// - /// Write the to the specified output stream. - /// - /// - /// Writes the multipart MIME entity and its subparts to the output stream. - /// - /// The formatting options. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override void WriteTo (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - base.WriteTo (options, stream, contentOnly, cancellationToken); - - if (ContentType.IsMimeType ("multipart", "signed")) { - // don't reformat the headers or content of any children of a multipart/signed - if (options.International || options.HiddenHeaders.Count > 0) { - options = options.Clone (); - options.HiddenHeaders.Clear (); - options.International = false; - } - } - - var cancellable = stream as ICancellableStream; - - if (RawPreamble != null && RawPreamble.Length > 0) - WriteBytes (options, stream, RawPreamble, children.Count > 0 || EnsureNewLine, cancellationToken); - - var boundary = Encoding.ASCII.GetBytes ("--" + Boundary + "--"); - - if (cancellable != null) { - for (int i = 0; i < children.Count; i++) { - var msg = children[i] as MessagePart; - var multi = children[i] as Multipart; - var part = children[i] as MimePart; - - cancellable.Write (boundary, 0, boundary.Length - 2, cancellationToken); - cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); - children[i].WriteTo (options, stream, false, cancellationToken); - - if (msg != null && msg.Message != null && msg.Message.Body != null) { - multi = msg.Message.Body as Multipart; - part = msg.Message.Body as MimePart; - } - - if ((part != null && part.Content == null) || - (multi != null && !multi.WriteEndBoundary)) - continue; - - cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); - } - - if (!WriteEndBoundary) - return; - - cancellable.Write (boundary, 0, boundary.Length, cancellationToken); - - if (RawEpilogue == null) - cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); - } else { - for (int i = 0; i < children.Count; i++) { - var rfc822 = children[i] as MessagePart; - var multi = children[i] as Multipart; - var part = children[i] as MimePart; - - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (boundary, 0, boundary.Length - 2); - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - children[i].WriteTo (options, stream, false, cancellationToken); - - if (rfc822 != null && rfc822.Message != null && rfc822.Message.Body != null) { - multi = rfc822.Message.Body as Multipart; - part = rfc822.Message.Body as MimePart; - } - - if ((part != null && part.Content == null) || - (multi != null && !multi.WriteEndBoundary)) - continue; - - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - - if (!WriteEndBoundary) - return; - - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (boundary, 0, boundary.Length); - - if (RawEpilogue == null) { - cancellationToken.ThrowIfCancellationRequested (); - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - } - - if (RawEpilogue != null && RawEpilogue.Length > 0) - WriteBytes (options, stream, RawEpilogue, EnsureNewLine, cancellationToken); - } - - /// - /// Asynchronously write the to the specified output stream. - /// - /// - /// Asynchronously writes the multipart MIME entity and its subparts to the output stream. - /// - /// An awaitable task. - /// The formatting options. - /// The output stream. - /// true if only the content should be written; otherwise, false. - /// The cancellation token. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// An I/O error occurred. - /// - public override async Task WriteToAsync (FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default (CancellationToken)) - { - await base.WriteToAsync (options, stream, contentOnly, cancellationToken).ConfigureAwait (false); - - if (ContentType.IsMimeType ("multipart", "signed")) { - // don't hide or reformat the headers of any children of a multipart/signed - if (options.International || options.HiddenHeaders.Count > 0) { - options = options.Clone (); - options.HiddenHeaders.Clear (); - options.International = false; - } - } - - if (RawPreamble != null && RawPreamble.Length > 0) - await WriteBytesAsync (options, stream, RawPreamble, children.Count > 0 || EnsureNewLine, cancellationToken).ConfigureAwait (false); - - var boundary = Encoding.ASCII.GetBytes ("--" + Boundary + "--"); - - for (int i = 0; i < children.Count; i++) { - var msg = children[i] as MessagePart; - var multi = children[i] as Multipart; - var part = children[i] as MimePart; - - await stream.WriteAsync (boundary, 0, boundary.Length - 2, cancellationToken).ConfigureAwait (false); - await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); - await children[i].WriteToAsync (options, stream, false, cancellationToken).ConfigureAwait (false); - - if (msg != null && msg.Message != null && msg.Message.Body != null) { - multi = msg.Message.Body as Multipart; - part = msg.Message.Body as MimePart; - } - - if ((part != null && part.Content == null) || - (multi != null && !multi.WriteEndBoundary)) - continue; - - await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); - } - - if (!WriteEndBoundary) - return; - - await stream.WriteAsync (boundary, 0, boundary.Length, cancellationToken).ConfigureAwait (false); - - if (RawEpilogue == null) - await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false); - - if (RawEpilogue != null && RawEpilogue.Length > 0) - await WriteBytesAsync (options, stream, RawEpilogue, EnsureNewLine, cancellationToken).ConfigureAwait (false); - } - - #region ICollection implementation - - /// - /// Get the number of parts in the multipart. - /// - /// - /// Indicates the number of parts in the multipart. - /// - /// The number of parts in the multipart. - public int Count { - get { return children.Count; } - } - - /// - /// Get a value indicating whether this instance is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Add an entity to the multipart. - /// - /// - /// Adds the specified part to the multipart. - /// - /// The part to add. - /// - /// is null. - /// - public void Add (MimeEntity part) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - WriteEndBoundary = true; - children.Add (part); - } - - /// - /// Clear a multipart. - /// - /// - /// Removes all of the parts within the multipart. - /// - public void Clear () - { - WriteEndBoundary = true; - children.Clear (); - } - - /// - /// Check if the contains the specified part. - /// - /// - /// Determines whether or not the multipart contains the specified part. - /// - /// true if the specified part exists; - /// otherwise false. - /// The part to check for. - /// - /// is null. - /// - public bool Contains (MimeEntity part) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return children.Contains (part); - } - - /// - /// Copy all of the entities in the to the specified array. - /// - /// - /// Copies all of the entities within the into the array, - /// starting at the specified array index. - /// - /// The array to copy the headers to. - /// The index into the array. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void CopyTo (MimeEntity[] array, int arrayIndex) - { - children.CopyTo (array, arrayIndex); - } - - /// - /// Remove an entity from the multipart. - /// - /// - /// Removes the specified part, if it exists within the multipart. - /// - /// true if the part was removed; otherwise false. - /// The part to remove. - /// - /// is null. - /// - public bool Remove (MimeEntity part) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - if (!children.Remove (part)) - return false; - - WriteEndBoundary = true; - - return true; - } - - #endregion - - #region IList implementation - - /// - /// Get the index of an entity. - /// - /// - /// Finds the index of the specified part, if it exists. - /// - /// The index of the specified part if found; otherwise -1. - /// The part. - /// - /// is null. - /// - public int IndexOf (MimeEntity part) - { - if (part == null) - throw new ArgumentNullException (nameof (part)); - - return children.IndexOf (part); - } - - /// - /// Insert an entity into the at the specified index. - /// - /// - /// Inserts the part into the multipart at the specified index. - /// - /// The index. - /// The part. - /// - /// is null. - /// - /// - /// is out of range. - /// - public void Insert (int index, MimeEntity part) - { - if (index < 0 || index > children.Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (part == null) - throw new ArgumentNullException (nameof (part)); - - children.Insert (index, part); - WriteEndBoundary = true; - } - - /// - /// Remove an entity from the at the specified index. - /// - /// - /// Removes the entity at the specified index. - /// - /// The index. - /// - /// is out of range. - /// - public void RemoveAt (int index) - { - children.RemoveAt (index); - WriteEndBoundary = true; - } - - /// - /// Get or set the at the specified index. - /// - /// - /// Gets or sets the at the specified index. - /// - /// The entity at the specified index. - /// The index. - /// - /// is null. - /// - /// - /// is out of range. - /// - public MimeEntity this[int index] { - get { return children[index]; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - WriteEndBoundary = true; - children[index] = value; - } - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get the enumerator for the children of the . - /// - /// - /// Gets the enumerator for the children of the . - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return children.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get the enumerator for the children of the . - /// - /// - /// Gets the enumerator for the children of the . - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return children.GetEnumerator (); - } - - #endregion - } -} diff --git a/src/MimeKit/MultipartAlternative.cs b/src/MimeKit/MultipartAlternative.cs deleted file mode 100644 index 1fd29ff..0000000 --- a/src/MimeKit/MultipartAlternative.cs +++ /dev/null @@ -1,184 +0,0 @@ -// -// MultipartAlternative.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -using MimeKit.Text; - -namespace MimeKit { - /// - /// A multipart/alternative MIME entity. - /// - /// - /// A multipart/alternative MIME entity contains, as one might expect, is used to offer a list of - /// alternative formats for the main body of the message (usually they will be "text/plain" and - /// "text/html"). These alternatives are in order of increasing faithfulness to the original document - /// (in other words, the last entity will be in a format that, when rendered, will most closely match - /// what the sending client's WYSISYG editor produced). - /// - public class MultipartAlternative : Multipart - { - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MultipartAlternative (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new part. - /// - /// An array of initialization parameters: headers and MIME entities. - /// - /// is null. - /// - /// - /// contains one or more arguments of an unknown type. - /// - public MultipartAlternative (params object[] args) : base ("alternative", args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new part. - /// - public MultipartAlternative () : base ("alternative") - { - } - - /// - /// Get the text of the text/plain alternative. - /// - /// - /// Gets the text of the text/plain alternative, if it exists. - /// - /// The text if a text/plain alternative exists; otherwise, null. - public string TextBody { - get { return GetTextBody (TextFormat.Plain); } - } - - /// - /// Get the HTML-formatted text of the text/html alternative. - /// - /// - /// Gets the HTML-formatted text of the text/html alternative, if it exists. - /// - /// The HTML if a text/html alternative exists; otherwise, null. - public string HtmlBody { - get { return GetTextBody (TextFormat.Html); } - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMultipartAlternative (this); - } - - internal static string GetText (TextPart text) - { - if (text.IsFlowed) { - var converter = new FlowedToText (); - string delsp; - - if (text.ContentType.Parameters.TryGetValue ("delsp", out delsp)) - converter.DeleteSpace = delsp.ToLowerInvariant () == "yes"; - - return converter.Convert (text.Text); - } - - return text.Text; - } - - /// - /// Get the text body in the specified format. - /// - /// - /// Gets the text body in the specified format, if it exists. - /// - /// The text body in the desired format if it exists; otherwise, null. - /// The desired text format. - public string GetTextBody (TextFormat format) - { - // walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful - for (int i = Count - 1; i >= 0; i--) { - var alternative = this[i] as MultipartAlternative; - - if (alternative != null) { - // Note: nested multipart/alternative parts make no sense... yet here we are. - return alternative.GetTextBody (format); - } - - var related = this[i] as MultipartRelated; - var text = this[i] as TextPart; - - if (related != null) { - var root = related.Root; - - alternative = root as MultipartAlternative; - if (alternative != null) - return alternative.GetTextBody (format); - - text = root as TextPart; - } - - if (text != null && text.IsFormat (format)) - return GetText (text); - } - - return null; - } - } -} diff --git a/src/MimeKit/MultipartRelated.cs b/src/MimeKit/MultipartRelated.cs deleted file mode 100644 index 7522c16..0000000 --- a/src/MimeKit/MultipartRelated.cs +++ /dev/null @@ -1,344 +0,0 @@ -// -// MultipartRelated.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; - -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A multipart/related MIME entity. - /// - /// - /// A multipart/related MIME entity contains, as one might expect, inter-related MIME parts which - /// typically reference each other via URIs based on the Content-Id and/or Content-Location headers. - /// - /// - /// - /// - public class MultipartRelated : Multipart - { - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MultipartRelated (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new part. - /// - /// An array of initialization parameters: headers and MIME entities. - /// - /// is null. - /// - /// - /// contains one or more arguments of an unknown type. - /// - public MultipartRelated (params object[] args) : base ("related", args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new part. - /// - public MultipartRelated () : base ("related") - { - } - - int GetRootIndex () - { - var start = ContentType.Parameters["start"]; - - if (start != null) { - string contentId; - - if ((contentId = MimeUtils.EnumerateReferences (start).FirstOrDefault ()) == null) - contentId = start; - - var cid = new Uri (string.Format ("cid:{0}", contentId)); - - return IndexOf (cid); - } - - var type = ContentType.Parameters["type"]; - - if (type == null) - return -1; - - for (int index = 0; index < Count; index++) { - var mimeType = this[index].ContentType.MimeType; - - if (mimeType.Equals (type, StringComparison.OrdinalIgnoreCase)) - return index; - } - - return -1; - } - - /// - /// Gets or sets the root document of the multipart/related part and the appropriate Content-Type parameters. - /// - /// - /// Gets or sets the root document that references the other MIME parts within the multipart/related. - /// When getting the root document, the "start" parameter of the Content-Type header is used to - /// determine which of the parts is the root. If the "start" parameter does not exist or does not reference - /// any of the child parts, then the first child is assumed to be the root. - /// When setting the root document MIME part, the Content-Type header of the multipart/related part is also - /// updated with a appropriate "start" and "type" parameters. - /// - /// - /// - /// - /// The root MIME part. - /// - /// is null. - /// - public MimeEntity Root { - get { - int index = GetRootIndex (); - - if (index < 0 && Count == 0) - return null; - - return this[Math.Max (index, 0)]; - } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - int index; - - if (Count > 0) { - if ((index = GetRootIndex ()) != -1) { - this[index] = value; - } else { - Insert (0, value); - index = 0; - } - } else { - Add (value); - index = 0; - } - - if (string.IsNullOrEmpty (value.ContentId)) - value.ContentId = MimeUtils.GenerateMessageId (); - - ContentType.Parameters["type"] = value.ContentType.MediaType + "/" + value.ContentType.MediaSubtype; - - // Note: we only use a "start" parameter if the index of the root entity is not at index 0 in order - // to work around the following Thunderbird bug: https://bugzilla.mozilla.org/show_bug.cgi?id=471402 - if (index > 0) - ContentType.Parameters["start"] = "<" + value.ContentId + ">"; - else - ContentType.Parameters.Remove ("start"); - } - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMultipartRelated (this); - } - - /// - /// Checks if the contains a part matching the specified URI. - /// - /// - /// Determines whether or not the multipart/related entity contains a part matching the specified URI. - /// - /// true if the specified part exists; otherwise false. - /// The URI of the MIME part. - /// - /// is null. - /// - public bool Contains (Uri uri) - { - return IndexOf (uri) != -1; - } - - /// - /// Gets the index of the part matching the specified URI. - /// - /// - /// Finds the index of the part matching the specified URI, if it exists. - /// If the URI scheme is "cid", then matching is performed based on the Content-Id header - /// values, otherwise the Content-Location headers are used. If the provided URI is absolute and a child - /// part's Content-Location is relative, then then the child part's Content-Location URI will be combined - /// with the value of its Content-Base header, if available, otherwise it will be combined with the - /// multipart/related part's Content-Base header in order to produce an absolute URI that can be - /// compared with the provided absolute URI. - /// - /// - /// - /// - /// The index of the part matching the specified URI if found; otherwise -1. - /// The URI of the MIME part. - /// - /// is null. - /// - public int IndexOf (Uri uri) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - bool cid = uri.IsAbsoluteUri && uri.Scheme.ToLowerInvariant () == "cid"; - - for (int index = 0; index < Count; index++) { - var entity = this[index]; - - if (uri.IsAbsoluteUri) { - if (cid) { - if (entity.ContentId == uri.AbsolutePath) - return index; - } else if (entity.ContentLocation != null) { - Uri absolute; - - if (!entity.ContentLocation.IsAbsoluteUri) { - if (entity.ContentBase != null) { - absolute = new Uri (entity.ContentBase, entity.ContentLocation); - } else if (ContentBase != null) { - absolute = new Uri (ContentBase, entity.ContentLocation); - } else { - continue; - } - } else { - absolute = entity.ContentLocation; - } - - if (absolute == uri) - return index; - } - } else if (entity.ContentLocation == uri) { - return index; - } - } - - return -1; - } - - /// - /// Opens a stream for reading the decoded content of the MIME part specified by the provided URI. - /// - /// - /// Opens a stream for reading the decoded content of the MIME part specified by the provided URI. - /// - /// A stream for reading the decoded content of the MIME part specified by the provided URI. - /// The URI. - /// The mime-type of the content. - /// The charset of the content (if the content is text-based) - /// - /// is null. - /// - /// - /// The MIME part for the specified URI could not be found. - /// - public Stream Open (Uri uri, out string mimeType, out string charset) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - int index = IndexOf (uri); - - if (index == -1) - throw new FileNotFoundException (); - - var part = this[index] as MimePart; - - if (part == null || part.Content == null) - throw new FileNotFoundException (); - - mimeType = part.ContentType.MimeType; - charset = part.ContentType.Charset; - - return part.Content.Open (); - } - - /// - /// Opens a stream for reading the decoded content of the MIME part specified by the provided URI. - /// - /// - /// Opens a stream for reading the decoded content of the MIME part specified by the provided URI. - /// - /// A stream for reading the decoded content of the MIME part specified by the provided URI. - /// The URI. - /// - /// is null. - /// - /// - /// The MIME part for the specified URI could not be found. - /// - public Stream Open (Uri uri) - { - if (uri == null) - throw new ArgumentNullException (nameof (uri)); - - int index = IndexOf (uri); - - if (index == -1) - throw new FileNotFoundException (); - - var part = this[index] as MimePart; - - if (part == null || part.Content == null) - throw new FileNotFoundException (); - - return part.Content.Open (); - } - } -} diff --git a/src/MimeKit/MultipartReport.cs b/src/MimeKit/MultipartReport.cs deleted file mode 100644 index 5754f09..0000000 --- a/src/MimeKit/MultipartReport.cs +++ /dev/null @@ -1,151 +0,0 @@ -// -// MultipartReport.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit { - /// - /// A multipart/report MIME entity. - /// - /// - /// A multipart/related MIME entity is a general container part for electronic mail - /// reports of any kind. - /// - /// - /// - /// - /// - /// - public class MultipartReport : Multipart - { - /// - /// Initialize a new instance of the class. - /// - /// - /// This constructor is used by . - /// - /// Information used by the constructor. - /// - /// is null. - /// - public MultipartReport (MimeEntityConstructorArgs args) : base (args) - { - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new part. - /// - /// The type of the report. - /// An array of initialization parameters: headers and MIME entities. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// contains one or more arguments of an unknown type. - /// - public MultipartReport (string reportType, params object[] args) : base ("report", args) - { - if (reportType == null) - throw new ArgumentNullException (nameof (reportType)); - - ReportType = reportType; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new part. - /// - /// The type of the report. - /// - /// is null. - /// - public MultipartReport (string reportType) : base ("report") - { - if (reportType == null) - throw new ArgumentNullException (nameof (reportType)); - - ReportType = reportType; - } - - /// - /// Gets or sets the type of the report. - /// - /// - /// Gets or sets the type of the report. - /// The report type should be the subtype of the second - /// of the multipart/report. - /// - /// - /// - /// - /// The type of the report. - /// - /// is null. - /// - public string ReportType { - get { return ContentType.Parameters["report-type"]; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (ReportType == value) - return; - - ContentType.Parameters["report-type"] = value.Trim (); - } - } - - /// - /// Dispatches to the specific visit method for this MIME entity. - /// - /// - /// This default implementation for nodes - /// calls . Override this - /// method to call into a more specific method on a derived visitor class - /// of the class. However, it should still - /// support unknown visitors by calling - /// . - /// - /// The visitor. - /// - /// is null. - /// - public override void Accept (MimeVisitor visitor) - { - if (visitor == null) - throw new ArgumentNullException (nameof (visitor)); - - visitor.VisitMultipartReport (this); - } - } -} diff --git a/src/MimeKit/Parameter.cs b/src/MimeKit/Parameter.cs deleted file mode 100644 index d773e86..0000000 --- a/src/MimeKit/Parameter.cs +++ /dev/null @@ -1,713 +0,0 @@ -// -// Parameter.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; - -using MimeKit.Encodings; -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A header parameter as found in the Content-Type and Content-Disposition headers. - /// - /// - /// Content-Type and Content-Disposition headers often have parameters that specify - /// further information about how to interpret the content. - /// - public class Parameter - { - ParameterEncodingMethod encodingMethod; - Encoding encoding; - string text; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new parameter with the specified name and value. - /// - /// The parameter name. - /// The parameter value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// contains illegal characters. - /// - public Parameter (string name, string value) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (name.Length == 0) - throw new ArgumentException ("Parameter names are not allowed to be empty.", nameof (name)); - - for (int i = 0; i < name.Length; i++) { - if (name[i] > 127 || !IsAttr ((byte) name[i])) - throw new ArgumentException ("Illegal characters in parameter name.", nameof (name)); - } - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Value = value; - Name = name; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new parameter with the specified name and value. - /// - /// The character encoding. - /// The parameter name. - /// The parameter value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// contains illegal characters. - /// - public Parameter (Encoding encoding, string name, string value) - { - if (encoding == null) - throw new ArgumentNullException (nameof (encoding)); - - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (name.Length == 0) - throw new ArgumentException ("Parameter names are not allowed to be empty.", nameof (name)); - - for (int i = 0; i < name.Length; i++) { - if (name[i] > 127 || !IsAttr ((byte) name[i])) - throw new ArgumentException ("Illegal characters in parameter name.", nameof (name)); - } - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Encoding = encoding; - Value = value; - Name = name; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new parameter with the specified name and value. - /// - /// The character encoding. - /// The parameter name. - /// The parameter value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// contains illegal characters. - /// - /// - /// is not supported. - /// - public Parameter (string charset, string name, string value) - { - if (charset == null) - throw new ArgumentNullException (nameof (charset)); - - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (name.Length == 0) - throw new ArgumentException ("Parameter names are not allowed to be empty.", nameof (name)); - - for (int i = 0; i < name.Length; i++) { - if (name[i] > 127 || !IsAttr ((byte) name[i])) - throw new ArgumentException ("Illegal characters in parameter name.", nameof (name)); - } - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Encoding = CharsetUtils.GetEncoding (charset); - Value = value; - Name = name; - } - - /// - /// Gets the parameter name. - /// - /// - /// Gets the parameter name. - /// - /// The parameter name. - public string Name { - get; private set; - } - - /// - /// Gets or sets the parameter value character encoding. - /// - /// - /// Gets or sets the parameter value character encoding. - /// - /// The character encoding. - public Encoding Encoding { - get { return encoding ?? CharsetUtils.UTF8; } - set { - if (encoding == value) - return; - - encoding = value; - OnChanged (); - } - } - - /// - /// Gets or sets the parameter encoding method to use. - /// - /// - /// Gets or sets the parameter encoding method to use. - /// The MIME specifications specify that the proper method for encoding Content-Type - /// and Content-Disposition parameter values is the method described in - /// rfc2231. However, it is common for - /// some older email clients to improperly encode using the method described in - /// rfc2047 instead. - /// If set to , the encoding - /// method used will default to the value set on the . - /// - /// - /// - /// - /// The encoding method. - /// - /// is not a valid value. - /// - public ParameterEncodingMethod EncodingMethod { - get { return encodingMethod; } - set { - if (encodingMethod == value) - return; - - switch (value) { - case ParameterEncodingMethod.Default: - case ParameterEncodingMethod.Rfc2047: - case ParameterEncodingMethod.Rfc2231: - encodingMethod = value; - break; - default: - throw new ArgumentOutOfRangeException (nameof (value)); - } - - OnChanged (); - } - } - - /// - /// Gets or sets the parameter value. - /// - /// - /// Gets or sets the parameter value. - /// - /// The parameter value. - /// - /// is null. - /// - public string Value { - get { return text; } - set { - if (value == null) - throw new ArgumentNullException (nameof (value)); - - if (text == value) - return; - - text = value; - OnChanged (); - } - } - - static bool IsAttr (byte c) - { - return c.IsAttr (); - } - - static bool IsCtrl (char c) - { - return ((byte) c).IsCtrl (); - } - - enum EncodeMethod { - None, - Quote, - Rfc2047, - Rfc2231 - } - - EncodeMethod GetEncodeMethod (FormatOptions options, string name, string value, out string quoted) - { - var method = EncodeMethod.None; - EncodeMethod encode; - - switch (encodingMethod) { - default: - if (options.ParameterEncodingMethod == ParameterEncodingMethod.Rfc2231) - encode = EncodeMethod.Rfc2231; - else - encode = EncodeMethod.Rfc2047; - break; - case ParameterEncodingMethod.Rfc2231: - encode = EncodeMethod.Rfc2231; - break; - case ParameterEncodingMethod.Rfc2047: - encode = EncodeMethod.Rfc2047; - break; - } - - quoted = null; - - if (name.Length + 1 + value.Length >= options.MaxLineLength) - return encode; - - for (int i = 0; i < value.Length; i++) { - if (value[i] < 128) { - var c = (byte) value[i]; - - if (c.IsCtrl ()) - return encode; - - if (!c.IsAttr ()) - method = EncodeMethod.Quote; - } else if (options.International) { - method = EncodeMethod.Quote; - } else { - return encode; - } - } - - if (method == EncodeMethod.Quote) { - quoted = MimeUtils.Quote (value); - - if (name.Length + 1 + quoted.Length >= options.MaxLineLength) - return encode; - } - - return method; - } - - static EncodeMethod GetEncodeMethod (FormatOptions options, char[] value, int startIndex, int length) - { - var method = EncodeMethod.None; - - for (int i = startIndex; i < startIndex + length; i++) { - if (value[i] < 128) { - var c = (byte) value[i]; - - if (c.IsCtrl ()) - return EncodeMethod.Rfc2231; - - if (!c.IsAttr ()) - method = EncodeMethod.Quote; - } else if (options.International) { - method = EncodeMethod.Quote; - } else { - return EncodeMethod.Rfc2231; - } - } - - return method; - } - - static EncodeMethod GetEncodeMethod (byte[] value, int length) - { - var method = EncodeMethod.None; - - for (int i = 0; i < length; i++) { - if (value[i] >= 127 || value[i].IsCtrl ()) - return EncodeMethod.Rfc2231; - - if (!value[i].IsAttr ()) - method = EncodeMethod.Quote; - } - - return method; - } - - static Encoding GetBestEncoding (string value, Encoding defaultEncoding) - { - int encoding = 0; // us-ascii - - for (int i = 0; i < value.Length; i++) { - if (value[i] < 127) { - if (IsCtrl (value[i])) - encoding = Math.Max (encoding, 1); - } else if (value[i] < 256) { - encoding = Math.Max (encoding, 1); - } else { - encoding = 2; - } - } - - switch (encoding) { - case 0: return Encoding.ASCII; - case 1: return Encoding.GetEncoding (28591); // iso-8859-1 - default: return defaultEncoding; - } - } - - static bool Rfc2231GetNextValue (FormatOptions options, string charset, Encoder encoder, HexEncoder hex, char[] chars, ref int index, ref byte[] bytes, ref byte[] encoded, int maxLength, out string value) - { - int length = chars.Length - index; - - if (length < maxLength) { - switch (GetEncodeMethod (options, chars, index, length)) { - case EncodeMethod.Quote: - value = MimeUtils.Quote (new string (chars, index, length)); - index += length; - return false; - case EncodeMethod.None: - value = new string (chars, index, length); - index += length; - return false; - } - } - - length = Math.Min (maxLength, length); - int ratio, count, n; - - do { - count = encoder.GetByteCount (chars, index, length, true); - if (count > maxLength && length > 1) { - if ((ratio = (int) Math.Round ((double) count / (double) length)) > 1) - length -= Math.Max ((count - maxLength) / ratio, 1); - else - length--; - continue; - } - - if (bytes.Length < count) - Array.Resize (ref bytes, count); - - count = encoder.GetBytes (chars, index, length, bytes, 0, true); - - // Note: the first chunk needs to be encoded in order to declare the charset - if (index > 0 || charset == "us-ascii") { - var method = GetEncodeMethod (bytes, count); - - if (method == EncodeMethod.Quote) { - value = MimeUtils.Quote (Encoding.ASCII.GetString (bytes, 0, count)); - index += length; - return false; - } - - if (method == EncodeMethod.None) { - value = Encoding.ASCII.GetString (bytes, 0, count); - index += length; - return false; - } - } - - n = hex.EstimateOutputLength (count); - if (encoded.Length < n) - Array.Resize (ref encoded, n); - - // only the first value gets a charset declaration - int charsetLength = index == 0 ? charset.Length + 2 : 0; - - n = hex.Encode (bytes, 0, count, encoded); - if (n > 3 && (charsetLength + n) > maxLength) { - int x = 0; - - for (int i = n - 1; i >= 0 && charsetLength + i >= maxLength; i--) { - if (encoded[i] == (byte) '%') - x--; - else - x++; - } - - if ((ratio = (int) Math.Round ((double) count / (double) length)) > 1) - length -= Math.Max (x / ratio, 1); - else - length--; - continue; - } - - if (index == 0) - value = charset + "''" + Encoding.ASCII.GetString (encoded, 0, n); - else - value = Encoding.ASCII.GetString (encoded, 0, n); - index += length; - return true; - } while (true); - } - - void EncodeRfc2231 (FormatOptions options, StringBuilder builder, ref int lineLength, Encoding headerEncoding) - { - var bestEncoding = options.International ? CharsetUtils.UTF8 : GetBestEncoding (Value, encoding ?? headerEncoding); - int maxLength = options.MaxLineLength - (Name.Length + 6); - var charset = CharsetUtils.GetMimeCharset (bestEncoding); - var encoder = (Encoder) bestEncoding.GetEncoder (); - var bytes = new byte[Math.Max (maxLength, 6)]; - var hexbuf = new byte[bytes.Length * 3 + 3]; - var chars = Value.ToCharArray (); - var hex = new HexEncoder (); - int index = 0, i = 0; - string value, id; - bool encoded; - int length; - - do { - builder.Append (';'); - lineLength++; - - encoded = Rfc2231GetNextValue (options, charset, encoder, hex, chars, ref index, ref bytes, ref hexbuf, maxLength, out value); - length = Name.Length + (encoded ? 1 : 0) + 1 + value.Length; - - if (i == 0 && index == chars.Length) { - if (lineLength + 1 + length >= options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - } else { - builder.Append (' '); - lineLength++; - } - - builder.Append (Name); - if (encoded) - builder.Append ('*'); - builder.Append ('='); - builder.Append (value); - lineLength += length; - return; - } - - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - - id = i.ToString (); - length += id.Length + 1; - - builder.Append (Name); - builder.Append ('*'); - builder.Append (id); - if (encoded) - builder.Append ('*'); - builder.Append ('='); - builder.Append (value); - lineLength += length; - i++; - } while (index < chars.Length); - } - - static int EstimateEncodedWordLength (string charset, int byteCount, int encodeCount) - { - int length = charset.Length + 7; - - if ((double) encodeCount < (byteCount * 0.17)) { - // quoted-printable encoding - return length + (byteCount - encodeCount) + (encodeCount * 3); - } - - // base64 encoding - return length + ((byteCount + 2) / 3) * 4; - } - - static bool ExceedsMaxWordLength (string charset, int byteCount, int encodeCount, int maxLength) - { - int length = EstimateEncodedWordLength (charset, byteCount, encodeCount); - - return length + 1 >= maxLength; - } - - static int Rfc2047EncodeNextChunk (StringBuilder str, string text, ref int index, Encoding encoding, string charset, Encoder encoder, int maxLength) - { - int byteCount = 0, charCount = 0, encodeCount = 0; - var buffer = new char[2]; - int startIndex = index; - int nchars, n; - char c; - - while (index < text.Length) { - c = text[index++]; - - if (c < 127) { - if (IsCtrl (c) || c == '"' || c == '\\') - encodeCount++; - - byteCount++; - charCount++; - nchars = 1; - n = 1; - } else if (c < 256) { - // iso-8859-1 - encodeCount++; - byteCount++; - charCount++; - nchars = 1; - n = 1; - } else { - if (char.IsSurrogatePair (text, index - 1)) { - buffer[1] = text[index++]; - nchars = 2; - } else { - nchars = 1; - } - - buffer[0] = c; - - try { - n = encoder.GetByteCount (buffer, 0, nchars, true); - } catch { - n = 3; - } - - charCount += nchars; - encodeCount += n; - byteCount += n; - } - - if (ExceedsMaxWordLength (charset, byteCount, encodeCount, maxLength)) { - // restore our previous state - charCount -= nchars; - index -= nchars; - byteCount -= n; - break; - } - } - - return Rfc2047.AppendEncodedWord (str, encoding, text, startIndex, charCount, QEncodeMode.Text); - } - - void EncodeRfc2047 (FormatOptions options, StringBuilder builder, ref int lineLength, Encoding headerEncoding) - { - var bestEncoding = options.International ? CharsetUtils.UTF8 : GetBestEncoding (Value, encoding ?? headerEncoding); - var charset = CharsetUtils.GetMimeCharset (bestEncoding); - var encoder = (Encoder) bestEncoding.GetEncoder (); - int index = 0; - int length; - - builder.Append (';'); - lineLength++; - - // account for: + + "=\"=??b?<10 chars>?=\"" - if (lineLength + Name.Length + charset.Length + 10 + Math.Min (Value.Length, 10) >= options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - } else { - builder.Append (' '); - lineLength++; - } - - builder.AppendFormat ("{0}=\"", Name); - lineLength += Name.Length + 2; - - do { - length = Rfc2047EncodeNextChunk (builder, Value, ref index, bestEncoding, charset, encoder, (options.MaxLineLength - lineLength) - 1); - lineLength += length; - - if (index >= Value.Length) - break; - - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - } while (true); - - builder.Append ('\"'); - lineLength++; - } - - internal void Encode (FormatOptions options, StringBuilder builder, ref int lineLength, Encoding headerEncoding) - { - string quoted; - - switch (GetEncodeMethod (options, Name, Value, out quoted)) { - case EncodeMethod.Rfc2231: - EncodeRfc2231 (options, builder, ref lineLength, headerEncoding); - break; - case EncodeMethod.Rfc2047: - EncodeRfc2047 (options, builder, ref lineLength, headerEncoding); - break; - case EncodeMethod.None: - quoted = Value; - goto default; - default: - builder.Append (';'); - lineLength++; - - if (lineLength + 1 + Name.Length + 1 + quoted.Length >= options.MaxLineLength) { - builder.Append (options.NewLine); - builder.Append ('\t'); - lineLength = 1; - } else { - builder.Append (' '); - lineLength++; - } - - lineLength += Name.Length + 1 + quoted.Length; - builder.Append (Name); - builder.Append ('='); - builder.Append (quoted); - break; - } - } - - /// - /// Returns a string representation of the . - /// - /// - /// Formats the parameter name and value in the form name="value". - /// - /// A string representation of the . - public override string ToString () - { - return Name + "=" + MimeUtils.Quote (Value); - } - - internal event EventHandler Changed; - - void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - } -} diff --git a/src/MimeKit/ParameterEncodingMethod.cs b/src/MimeKit/ParameterEncodingMethod.cs deleted file mode 100644 index 3670f6e..0000000 --- a/src/MimeKit/ParameterEncodingMethod.cs +++ /dev/null @@ -1,58 +0,0 @@ -// -// ParameterEncodingMethod.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit { - /// - /// The method to use for encoding Content-Type and Content-Disposition parameter values. - /// - /// - /// The MIME specifications specify that the proper method for encoding Content-Type and - /// Content-Disposition parameter values is the method described in - /// rfc2231. However, it is common for - /// some older email clients to improperly encode using the method described in - /// rfc2047 instead. - /// - /// - /// - /// - public enum ParameterEncodingMethod { - /// - /// Use the default encoding method set on the . - /// - Default = 0, - - /// - /// Use the encoding method described in rfc2231. - /// - Rfc2231 = (1 << 0), - - /// - /// Use the encoding method described in rfc2047 (for compatibility with older, - /// non-rfc-compliant email clients). - /// - Rfc2047 = (1 << 1) - } -} diff --git a/src/MimeKit/ParameterList.cs b/src/MimeKit/ParameterList.cs deleted file mode 100644 index 6f2bd91..0000000 --- a/src/MimeKit/ParameterList.cs +++ /dev/null @@ -1,1092 +0,0 @@ -// -// ParameterList.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Collections; -using System.Collections.Generic; - -using MimeKit.Encodings; -using MimeKit.Utils; - -namespace MimeKit { - /// - /// A list of parameters, as found in the Content-Type and Content-Disposition headers. - /// - /// - /// Parameters are used by both and . - /// - public class ParameterList : IList - { - readonly Dictionary table; - readonly List parameters; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new parameter list. - /// - public ParameterList () - { - table = new Dictionary (MimeUtils.OrdinalIgnoreCase); - parameters = new List (); - } - - /// - /// Add a parameter with the specified name and value. - /// - /// - /// Adds a new parameter to the list with the specified name and value. - /// - /// The parameter name. - /// The parameter value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - public void Add (string name, string value) - { - Add (new Parameter (name, value)); - } - - /// - /// Add a parameter with the specified name and value. - /// - /// - /// Adds a new parameter to the list with the specified name and value. - /// - /// The character encoding. - /// The parameter name. - /// The parameter value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// contains illegal characters. - /// - public void Add (Encoding encoding, string name, string value) - { - Add (new Parameter (encoding, name, value)); - } - - /// - /// Add a parameter with the specified name and value. - /// - /// - /// Adds a new parameter to the list with the specified name and value. - /// - /// The character encoding. - /// The parameter name. - /// The parameter value. - /// - /// is null. - /// -or- - /// is null. - /// -or- - /// is null. - /// - /// - /// cannot be empty. - /// -or- - /// contains illegal characters. - /// - /// - /// is not supported. - /// - public void Add (string charset, string name, string value) - { - Add (new Parameter (charset, name, value)); - } - - /// - /// Check if the contains a parameter with the specified name. - /// - /// - /// Determines whether or not the parameter list contains a parameter with the specified name. - /// - /// true if the requested parameter exists; - /// otherwise false. - /// The parameter name. - /// - /// is null. - /// - public bool Contains (string name) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - return table.ContainsKey (name); - } - - /// - /// Get the index of the requested parameter, if it exists. - /// - /// - /// Finds the index of the parameter with the specified name, if it exists. - /// - /// The index of the requested parameter; otherwise -1. - /// The parameter name. - /// - /// is null. - /// - public int IndexOf (string name) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - for (int i = 0; i < parameters.Count; i++) { - if (name.Equals (parameters[i].Name, StringComparison.OrdinalIgnoreCase)) - return i; - } - - return -1; - } - - /// - /// Insert a parameter with the specified name and value at the given index. - /// - /// - /// Inserts a new parameter with the given name and value at the specified index. - /// - /// The index to insert the parameter. - /// The parameter name. - /// The parameter value. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - /// - /// is out of range. - /// - public void Insert (int index, string name, string value) - { - if (index < 0 || index > Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - Insert (index, new Parameter (name, value)); - } - - /// - /// Remove the specified parameter. - /// - /// - /// Removes the parameter with the specified name from the list, if it exists. - /// - /// true if the specified parameter was removed; - /// otherwise false. - /// The parameter name. - /// - /// is null. - /// - public bool Remove (string name) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - Parameter param; - if (!table.TryGetValue (name, out param)) - return false; - - return Remove (param); - } - - /// - /// Get or set the value of a parameter with the specified name. - /// - /// - /// Gets or sets the value of a parameter with the specified name. - /// - /// The value of the specified parameter if it exists; otherwise null. - /// The parameter name. - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// The contains illegal characters. - /// - public string this [string name] { - get { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - Parameter param; - if (table.TryGetValue (name, out param)) - return param.Value; - - return null; - } - set { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - Parameter param; - if (table.TryGetValue (name, out param)) { - param.Value = value; - } else { - Add (name, value); - } - } - } - - /// - /// Get the parameter with the specified name. - /// - /// - /// Gets the parameter with the specified name. - /// - /// - /// - /// - /// true if the parameter exists; otherwise, false. - /// The parameter name. - /// The parameter. - /// - /// is null. - /// - public bool TryGetValue (string name, out Parameter param) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - return table.TryGetValue (name, out param); - } - - /// - /// Get the value of the parameter with the specified name. - /// - /// - /// Gets the value of the parameter with the specified name. - /// - /// true if the parameter exists; otherwise, false. - /// The parameter name. - /// The parameter value. - /// - /// is null. - /// - public bool TryGetValue (string name, out string value) - { - Parameter param; - - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (!table.TryGetValue (name, out param)) { - value = null; - return false; - } - - value = param.Value; - - return true; - } - - #region ICollection implementation - - /// - /// Get the number of parameters in the . - /// - /// - /// Indicates the number of parameters in the list. - /// - /// The number of parameters. - public int Count { - get { return parameters.Count; } - } - - /// - /// Get a value indicating whether this instance is read only. - /// - /// - /// A is never read-only. - /// - /// true if this instance is read only; otherwise, false. - public bool IsReadOnly { - get { return false; } - } - - /// - /// Add a to a . - /// - /// - /// Adds the specified parameter to the end of the list. - /// - /// The parameter to add. - /// - /// The is null. - /// - /// - /// A parameter with the same name as - /// already exists. - /// - public void Add (Parameter param) - { - if (param == null) - throw new ArgumentNullException (nameof (param)); - - if (table.ContainsKey (param.Name)) - throw new ArgumentException ("A parameter of that name already exists.", nameof (param)); - - param.Changed += OnParamChanged; - table.Add (param.Name, param); - parameters.Add (param); - - OnChanged (); - } - - /// - /// Clear the parameter list. - /// - /// - /// Removes all of the parameters from the list. - /// - public void Clear () - { - foreach (var param in parameters) - param.Changed -= OnParamChanged; - - parameters.Clear (); - table.Clear (); - - OnChanged (); - } - - /// - /// Check if the contains the specified parameter. - /// - /// - /// Determines whether or not the parameter list contains the specified parameter. - /// - /// true if the specified parameter is contained; - /// otherwise false. - /// The parameter. - /// - /// The is null. - /// - public bool Contains (Parameter param) - { - if (param == null) - throw new ArgumentNullException (nameof (param)); - - return parameters.Contains (param); - } - - /// - /// Copy all of the parameters in the list to the specified array. - /// - /// - /// Copies all of the parameters within the into the array, - /// starting at the specified array index. - /// - /// The array to copy the parameters to. - /// The index into the array. - public void CopyTo (Parameter[] array, int arrayIndex) - { - parameters.CopyTo (array, arrayIndex); - } - - /// - /// Remove a from a . - /// - /// - /// Removes the specified parameter from the list. - /// - /// true if the specified parameter was removed; - /// otherwise false. - /// The parameter. - /// - /// The is null. - /// - public bool Remove (Parameter param) - { - if (param == null) - throw new ArgumentNullException (nameof (param)); - - if (!parameters.Remove (param)) - return false; - - param.Changed -= OnParamChanged; - table.Remove (param.Name); - - OnChanged (); - - return true; - } - - #endregion - - #region IList implementation - - /// - /// Ges the index of the requested parameter, if it exists. - /// - /// - /// Finds the index of the specified parameter, if it exists. - /// - /// The index of the requested parameter; otherwise -1. - /// The parameter. - /// - /// The is null. - /// - public int IndexOf (Parameter param) - { - if (param == null) - throw new ArgumentNullException (nameof (param)); - - return parameters.IndexOf (param); - } - - /// - /// Insert a at the specified index. - /// - /// - /// Inserts the parameter at the specified index in the list. - /// - /// The index to insert the parameter. - /// The parameter. - /// - /// The is null. - /// - /// - /// The is out of range. - /// - /// - /// A parameter with the same name as - /// already exists. - /// - public void Insert (int index, Parameter param) - { - if (index < 0 || index > Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (param == null) - throw new ArgumentNullException (nameof (param)); - - if (table.ContainsKey (param.Name)) - throw new ArgumentException ("A parameter of that name already exists.", nameof (param)); - - parameters.Insert (index, param); - table.Add (param.Name, param); - param.Changed += OnParamChanged; - - OnChanged (); - } - - /// - /// Remove the parameter at the specified index. - /// - /// - /// Removes the parameter at the specified index. - /// - /// The index. - /// - /// The is out of range. - /// - public void RemoveAt (int index) - { - if (index < 0 || index > Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - var param = parameters[index]; - - param.Changed -= OnParamChanged; - parameters.RemoveAt (index); - table.Remove (param.Name); - - OnChanged (); - } - - /// - /// Get or set the at the specified index. - /// - /// - /// Gets or sets the at the specified index. - /// - /// The parameter at the specified index. - /// The index. - /// - /// The is null. - /// - /// - /// The is out of range. - /// - /// - /// A parameter with the same name as - /// already exists. - /// - public Parameter this [int index] { - get { - return parameters[index]; - } - set { - if (index < 0 || index >= Count) - throw new ArgumentOutOfRangeException (nameof (index)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - var param = parameters[index]; - - if (param == value) - return; - - if (param.Name.Equals (value.Name, StringComparison.OrdinalIgnoreCase)) { - // replace the old param with the new one - if (table[param.Name] == param) - table[param.Name] = value; - } else if (table.ContainsKey (value.Name)) { - throw new ArgumentException ("A parameter of that name already exists.", nameof (value)); - } else { - table.Add (value.Name, value); - table.Remove (param.Name); - } - - param.Changed -= OnParamChanged; - value.Changed += OnParamChanged; - parameters[index] = value; - - OnChanged (); - } - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the list of parameters. - /// - /// - /// Gets an enumerator for the list of parameters. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return parameters.GetEnumerator (); - } - - #endregion - - #region IEnumerable implementation - - /// - /// Get an enumerator for the list of parameters. - /// - /// - /// Gets an enumerator for the list of parameters. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return parameters.GetEnumerator (); - } - - #endregion - - internal void Encode (FormatOptions options, StringBuilder builder, ref int lineLength, Encoding charset) - { - foreach (var param in parameters) - param.Encode (options, builder, ref lineLength, charset); - } - - /// - /// Serialize a to a string. - /// - /// - /// If there are multiple parameters in the list, they will be separated by a semicolon. - /// - /// A string representing the . - public override string ToString () - { - var values = new StringBuilder (); - - foreach (var param in parameters) { - values.Append ("; "); - values.Append (param.ToString ()); - } - - return values.ToString (); - } - - internal event EventHandler Changed; - - void OnParamChanged (object sender, EventArgs args) - { - OnChanged (); - } - - void OnChanged () - { - if (Changed != null) - Changed (this, EventArgs.Empty); - } - - static bool SkipParamName (byte[] text, ref int index, int endIndex) - { - int startIndex = index; - - while (index < endIndex && text[index].IsAttr ()) - index++; - - return index > startIndex; - } - - class NameValuePair : IComparable - { - public int ValueLength; - public int ValueStart; - public bool Encoded; - public byte[] Value; - public string Name; - public int? Id; - - #region IComparable implementation - public int CompareTo (NameValuePair other) - { - if (!Id.HasValue) - return other.Id.HasValue ? -1 : 0; - - if (!other.Id.HasValue) - return 1; - - return Id.Value - other.Id.Value; - } - #endregion - } - - static bool TryParseNameValuePair (ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out NameValuePair pair) - { - int valueIndex, valueLength, startIndex; - bool encoded = false; - int? id = null; - byte[] value; - string name; - - pair = null; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - startIndex = index; - if (!SkipParamName (text, ref index, endIndex)) { - if (throwOnError) - throw new ParseException (string.Format ("Invalid parameter name token at offset {0}", startIndex), startIndex, index); - - return false; - } - - name = Encoding.ASCII.GetString (text, startIndex, index - startIndex); - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (text[index] == (byte) '*') { - // the parameter is either encoded or it has a part id - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); - - return false; - } - - int identifier; - if (ParseUtils.TryParseInt32 (text, ref index, endIndex, out identifier)) { - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); - - return false; - } - - if (text[index] == (byte) '*') { - encoded = true; - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); - - return false; - } - } - - id = identifier; - } else { - encoded = true; - } - } - - if (text[index] != (byte) '=') { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); - - return false; - } - - index++; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) { - if (throwOnError) - throw new ParseException (string.Format ("Incomplete parameter at offset {0}", startIndex), startIndex, index); - - return false; - } - - valueIndex = index; - value = text; - - if (text[index] == (byte) '"') { - ParseUtils.SkipQuoted (text, ref index, endIndex, throwOnError); - valueLength = index - valueIndex; - } else if (options.ParameterComplianceMode == RfcComplianceMode.Strict) { - ParseUtils.SkipToken (text, ref index, endIndex); - valueLength = index - valueIndex; - } else { - // Note: Google Docs, for example, does not always quote name/filename parameters - // with spaces in the name. See https://github.com/jstedfast/MimeKit/issues/106 - // for details. - while (index < endIndex && text[index] != (byte) ';' && text[index] != (byte) '\r' && text[index] != (byte) '\n') - index++; - - valueLength = index - valueIndex; - - if (index < endIndex && text[index] != (byte) ';') { - // Note: https://github.com/jstedfast/MimeKit/issues/159 adds to this suckage - // by having a multi-line unquoted value with spaces... don't you just love - // mail software written by people who have never heard of standards? - using (var memory = new MemoryStream ()) { - memory.Write (text, valueIndex, valueLength); - - do { - while (index < endIndex && (text[index] == (byte) '\r' || text[index] == (byte) '\n')) - index++; - - valueIndex = index; - - while (index < endIndex && text[index] != (byte) ';' && text[index] != (byte) '\r' && text[index] != (byte) '\n') - index++; - - memory.Write (text, valueIndex, index - valueIndex); - } while (index < endIndex && text[index] != ';'); - - value = memory.ToArray (); - valueLength = value.Length; - valueIndex = 0; - } - } - - // Trim trailing white space characters to work around issues such as the - // one described in https://github.com/jstedfast/MimeKit/issues/278 - while (valueLength > valueIndex && value[valueLength - 1].IsWhitespace ()) - valueLength--; - } - - pair = new NameValuePair { - ValueLength = valueLength, - ValueStart = valueIndex, - Encoded = encoded, - Value = value, - Name = name, - Id = id - }; - - return true; - } - - static bool TryGetCharset (byte[] text, ref int index, int endIndex, out string charset) - { - int startIndex = index; - int charsetEnd; - int i; - - charset = null; - - for (i = index; i < endIndex; i++) { - if (text[i] == (byte) '\'') - break; - } - - if (i == startIndex || i == endIndex) - return false; - - charsetEnd = i; - - for (i++; i < endIndex; i++) { - if (text[i] == (byte) '\'') - break; - } - - if (i == endIndex) - return false; - - charset = Encoding.ASCII.GetString (text, startIndex, charsetEnd - startIndex); - index = i + 1; - - return true; - } - - static string DecodeRfc2231 (out Encoding encoding, ref Decoder decoder, HexDecoder hex, byte[] text, int startIndex, int count, bool flush) - { - int endIndex = startIndex + count; - int index = startIndex; - string charset; - - // Note: decoder is only null if this is the first segment - if (decoder == null) { - if (TryGetCharset (text, ref index, endIndex, out charset)) { - try { - encoding = CharsetUtils.GetEncoding (charset, "?"); - decoder = (Decoder) encoding.GetDecoder (); - } catch (NotSupportedException) { - encoding = Encoding.GetEncoding (28591); // iso-8859-1 - decoder = (Decoder) encoding.GetDecoder (); - } - } else { - // When no charset is specified, it should be safe to assume US-ASCII... - // but we all know what assume means, right?? - encoding = Encoding.GetEncoding (28591); // iso-8859-1 - decoder = (Decoder) encoding.GetDecoder (); - } - } else { - encoding = null; - } - - int length = endIndex - index; - var decoded = new byte[hex.EstimateOutputLength (length)]; - - // hex decode... - length = hex.Decode (text, index, length, decoded); - - int outLength = decoder.GetCharCount (decoded, 0, length, flush); - var output = new char[outLength]; - - outLength = decoder.GetChars (decoded, 0, length, output, 0, flush); - - return new string (output, 0, outLength); - } - - internal static bool TryParse (ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out ParameterList paramList) - { - var rfc2231 = new Dictionary> (MimeUtils.OrdinalIgnoreCase); - var @params = new List (); - List parts; - - paramList = null; - - do { - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (index >= endIndex) - break; - - // handle empty parameter name/value pairs - if (text[index] == (byte) ';') { - index++; - continue; - } - - NameValuePair pair; - if (!TryParseNameValuePair (options, text, ref index, endIndex, throwOnError, out pair)) - return false; - - if (!ParseUtils.SkipCommentsAndWhiteSpace (text, ref index, endIndex, throwOnError)) - return false; - - if (pair.Id.HasValue) { - if (rfc2231.TryGetValue (pair.Name, out parts)) { - parts.Add (pair); - } else { - parts = new List (); - rfc2231[pair.Name] = parts; - @params.Add (pair); - parts.Add (pair); - } - } else { - @params.Add (pair); - } - - if (index >= endIndex) - break; - - if (text[index] != (byte) ';') { - if (throwOnError) - throw new ParseException (string.Format ("Invalid parameter list token at offset {0}", index), index, index); - - return false; - } - - index++; - } while (true); - - paramList = new ParameterList (); - var hex = new HexDecoder (); - - foreach (var param in @params) { - var method = ParameterEncodingMethod.Default; - int startIndex = param.ValueStart; - int length = param.ValueLength; - var buffer = param.Value; - Encoding encoding = null; - Decoder decoder = null; - Parameter parameter; - string value; - - if (param.Id.HasValue) { - method = ParameterEncodingMethod.Rfc2231; - parts = rfc2231[param.Name]; - parts.Sort (); - - value = string.Empty; - - for (int i = 0; i < parts.Count; i++) { - startIndex = parts[i].ValueStart; - length = parts[i].ValueLength; - buffer = parts[i].Value; - - if (parts[i].Encoded) { - bool flush = i + 1 >= parts.Count || !parts[i + 1].Encoded; - Encoding charset; - - // Note: Some mail clients mistakenly quote encoded parameter values when they shouldn't - if (length >= 2 && buffer[startIndex] == (byte) '"' && buffer[startIndex + length - 1] == (byte) '"') { - startIndex++; - length -= 2; - } - - value += DecodeRfc2231 (out charset, ref decoder, hex, buffer, startIndex, length, flush); - encoding = encoding ?? charset; - } else if (length >= 2 && buffer[startIndex] == (byte) '"') { - var quoted = CharsetUtils.ConvertToUnicode (options,buffer, startIndex, length); - value += MimeUtils.Unquote (quoted); - hex.Reset (); - } else if (length > 0) { - value += CharsetUtils.ConvertToUnicode (options, buffer, startIndex, length); - hex.Reset (); - } - } - hex.Reset (); - } else if (param.Encoded) { - // Note: param value is not supposed to be quoted, but issue #239 illustrates - // that this can happen in the wild. Hopefully we will not need to worry - // about quoted-pairs. - if (length >= 2 && buffer[startIndex] == (byte) '"') { - if (buffer[startIndex + length - 1] == (byte) '"') - length--; - - startIndex++; - length--; - } - - value = DecodeRfc2231 (out encoding, ref decoder, hex, buffer, startIndex, length, true); - method = ParameterEncodingMethod.Rfc2231; - hex.Reset (); - } else if (!paramList.Contains (param.Name)) { - // Note: If we've got an rfc2231-encoded version of the same parameter, then - // we'll want to choose that one as opposed to the ASCII variant (i.e. this one). - // - // While most mail clients that I know of do not send multiple parameters of the - // same name, rfc6266 suggests that HTTP servers are using this approach to work - // around HTTP clients that do not (yet) implement support for the rfc2231 - // encoding of parameter values. Since none of the MIME specifications provide - // any suggestions for dealing with this, following rfc6266 seems to make the - // most sense, even though it is meant for HTTP clients and servers. - int codepage = -1; - - if (length >= 2 && text[startIndex] == (byte) '"') { - var quoted = Rfc2047.DecodeText (options, buffer, startIndex, length, out codepage); - value = MimeUtils.Unquote (quoted); - } else if (length > 0) { - value = Rfc2047.DecodeText (options, buffer, startIndex, length, out codepage); - } else { - value = string.Empty; - } - - if (codepage != -1 && codepage != 65001) { - encoding = CharsetUtils.GetEncoding (codepage); - method = ParameterEncodingMethod.Rfc2047; - } - } else { - continue; - } - - if (paramList.table.TryGetValue (param.Name, out parameter)) { - parameter.Encoding = encoding; - parameter.Value = value; - } else if (encoding != null) { - paramList.Add (encoding, param.Name, value); - parameter = paramList[paramList.Count - 1]; - } else { - paramList.Add (param.Name, value); - parameter = paramList[paramList.Count - 1]; - } - - parameter.EncodingMethod = method; - } - - return true; - } - } -} diff --git a/src/MimeKit/ParseException.cs b/src/MimeKit/ParseException.cs deleted file mode 100644 index 1e87e66..0000000 --- a/src/MimeKit/ParseException.cs +++ /dev/null @@ -1,146 +0,0 @@ -// -// ParseException.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -#if SERIALIZABLE -using System.Security; -using System.Runtime.Serialization; -#endif - -namespace MimeKit { - /// - /// A Parse exception as thrown by the various Parse methods in MimeKit. - /// - /// - /// A can be thrown by any of the Parse() methods - /// in MimeKit. Each exception instance will have a - /// which marks the byte offset of the token that failed to parse as well - /// as a which marks the byte offset where the error - /// occurred. - /// -#if SERIALIZABLE - [Serializable] -#endif - public class ParseException : FormatException - { -#if SERIALIZABLE - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The serialization info. - /// The stream context. - /// - /// is null. - /// - protected ParseException (SerializationInfo info, StreamingContext context) : base (info, context) - { - TokenIndex = info.GetInt32 ("TokenIndex"); - ErrorIndex = info.GetInt32 ("ErrorIndex"); - } -#endif - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The byte offset of the token. - /// The byte offset of the error. - /// The inner exception. - public ParseException (string message, int tokenIndex, int errorIndex, Exception innerException) : base (message, innerException) - { - TokenIndex = tokenIndex; - ErrorIndex = errorIndex; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The error message. - /// The byte offset of the token. - /// The byte offset of the error. - public ParseException (string message, int tokenIndex, int errorIndex) : base (message) - { - TokenIndex = tokenIndex; - ErrorIndex = errorIndex; - } - -#if SERIALIZABLE - /// - /// When overridden in a derived class, sets the - /// with information about the exception. - /// - /// - /// Sets the - /// with information about the exception. - /// - /// The serialization info. - /// The streaming context. - /// - /// is null. - /// - [SecurityCritical] - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - - info.AddValue ("TokenIndex", TokenIndex); - info.AddValue ("ErrorIndex", ErrorIndex); - } -#endif - - /// - /// Gets the byte index of the token that was malformed. - /// - /// - /// The token index is the byte offset at which the token started. - /// - /// The byte index of the token. - public int TokenIndex { - get; private set; - } - - /// - /// Gets the index of the byte that caused the error. - /// - /// - /// The error index is the byte offset at which the parser encountered an error. - /// - /// The index of the byte that caused error. - public int ErrorIndex { - get; private set; - } - } -} diff --git a/src/MimeKit/ParserOptions.cs b/src/MimeKit/ParserOptions.cs deleted file mode 100644 index e851f11..0000000 --- a/src/MimeKit/ParserOptions.cs +++ /dev/null @@ -1,405 +0,0 @@ -// -// ParserOptions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Text; -using System.Reflection; -using System.Collections.Generic; - -#if ENABLE_CRYPTO -using MimeKit.Cryptography; -#endif - -using MimeKit.Tnef; -using MimeKit.Utils; - -namespace MimeKit { - /// - /// Parser options as used by as well as various Parse and TryParse methods in MimeKit. - /// - /// - /// allows you to change and/or override default parsing options used by methods such - /// as and others. - /// - public class ParserOptions - { - readonly Dictionary mimeTypes = new Dictionary (StringComparer.Ordinal); - static readonly Type[] ConstructorArgTypes = { typeof (MimeEntityConstructorArgs) }; - - /// - /// The default parser options. - /// - /// - /// If a is not supplied to or other Parse and TryParse - /// methods throughout MimeKit, will be used. - /// - public static readonly ParserOptions Default = new ParserOptions (); - - /// - /// Gets or sets the compliance mode that should be used when parsing rfc822 addresses. - /// - /// - /// In general, you'll probably want this value to be - /// (the default) as it allows maximum interoperability with existing (broken) mail clients - /// and other mail software such as sloppily written perl scripts (aka spambots). - /// Even in mode, the address parser - /// is fairly liberal in what it accepts. Setting it to - /// just makes it try harder to deal with garbage input. - /// - /// The RFC compliance mode. - public RfcComplianceMode AddressParserComplianceMode { get; set; } - - /// - /// Gets or sets whether the rfc822 address parser should ignore unquoted commas in address names. - /// - /// - /// In general, you'll probably want this value to be true (the default) as it allows - /// maximum interoperability with existing (broken) mail clients and other mail software such as - /// sloppily written perl scripts (aka spambots) that do not properly quote the name when it - /// contains a comma. - /// - /// true if the address parser should ignore unquoted commas in address names; otherwise, false. - public bool AllowUnquotedCommasInAddresses { get; set; } - - /// - /// Gets or sets whether the rfc822 address parser should allow addresses without a domain. - /// - /// - /// In general, you'll probably want this value to be true (the default) as it allows - /// maximum interoperability with older email messages that may contain local UNIX addresses. - /// This option exists in order to allow parsing of mailbox addresses that do not have an - /// @domain component. These types of addresses are rare and were typically only used when sending - /// mail to other users on the same UNIX system. - /// - /// true if the address parser should allow mailbox addresses without a domain; otherwise, false. - public bool AllowAddressesWithoutDomain { get; set; } - - /// - /// Gets or sets the maximum address group depth the parser should accept. - /// - /// - /// This option exists in order to define the maximum recursive depth of an rfc822 group address - /// that the parser should accept before bailing out with the assumption that the address is maliciously - /// formed. If the value is set too large, then it is possible that a maliciously formed set of - /// recursive group addresses could cause a stack overflow. - /// - /// The maximum address group depth. - public int MaxAddressGroupDepth { get; set; } - - /// - /// Gets or sets the maximum MIME nesting depth the parser should accept. - /// - /// - /// This option exists in order to define the maximum recursive depth of MIME parts that the parser - /// should accept before treating further nesting as a leaf-node MIME part and not recursing any further. - /// If the value is set too large, then it is possible that a maliciously formed set of rdeeply nested - /// multipart MIME parts could cause a stack overflow. - /// - /// The maximum MIME nesting depth. - public int MaxMimeDepth { get; set; } - - /// - /// Gets or sets the compliance mode that should be used when parsing Content-Type and Content-Disposition parameters. - /// - /// - /// In general, you'll probably want this value to be - /// (the default) as it allows maximum interoperability with existing (broken) mail clients - /// and other mail software such as sloppily written perl scripts (aka spambots). - /// Even in mode, the parameter parser - /// is fairly liberal in what it accepts. Setting it to - /// just makes it try harder to deal with garbage input. - /// - /// The RFC compliance mode. - public RfcComplianceMode ParameterComplianceMode { get; set; } - - /// - /// Gets or sets the compliance mode that should be used when decoding rfc2047 encoded words. - /// - /// - /// In general, you'll probably want this value to be - /// (the default) as it allows maximum interoperability with existing (broken) mail clients - /// and other mail software such as sloppily written perl scripts (aka spambots). - /// - /// The RFC compliance mode. - public RfcComplianceMode Rfc2047ComplianceMode { get; set; } - - /// - /// Gets or sets a value indicating whether the Content-Length value should be - /// respected when parsing mbox streams. - /// - /// - /// For more details about why this may be useful, you can find more information - /// at - /// http://www.jwz.org/doc/content-length.html. - /// - /// true if the Content-Length value should be respected; - /// otherwise, false. - public bool RespectContentLength { get; set; } - - /// - /// Gets or sets the charset encoding to use as a fallback for 8bit headers. - /// - /// - /// and - /// - /// use this charset encoding as a fallback when decoding 8bit text into unicode. The first - /// charset encoding attempted is UTF-8, followed by this charset encoding, before finally - /// falling back to iso-8859-1. - /// - /// The charset encoding. - public Encoding CharsetEncoding { get; set; } - - /// - /// Initialize a new instance of the class. - /// - /// - /// By default, new instances of enable rfc2047 work-arounds - /// (which are needed for maximum interoperability with mail software used in the wild) - /// and do not respect the Content-Length header value. - /// - public ParserOptions () - { - AddressParserComplianceMode = RfcComplianceMode.Loose; - ParameterComplianceMode = RfcComplianceMode.Loose; - Rfc2047ComplianceMode = RfcComplianceMode.Loose; - CharsetEncoding = CharsetUtils.UTF8; - AllowUnquotedCommasInAddresses = true; - AllowAddressesWithoutDomain = true; - RespectContentLength = false; - MaxAddressGroupDepth = 3; - MaxMimeDepth = 1024; - } - - /// - /// Clones an instance of . - /// - /// - /// Clones a set of options, allowing you to change a specific option - /// without requiring you to change the original. - /// - /// An identical copy of the current instance. - public ParserOptions Clone () - { - var options = new ParserOptions (); - options.AddressParserComplianceMode = AddressParserComplianceMode; - options.AllowUnquotedCommasInAddresses = AllowUnquotedCommasInAddresses; - options.AllowAddressesWithoutDomain = AllowAddressesWithoutDomain; - options.ParameterComplianceMode = ParameterComplianceMode; - options.Rfc2047ComplianceMode = Rfc2047ComplianceMode; - options.MaxAddressGroupDepth = MaxAddressGroupDepth; - options.RespectContentLength = RespectContentLength; - options.CharsetEncoding = CharsetEncoding; - options.MaxMimeDepth = MaxMimeDepth; - - foreach (var mimeType in mimeTypes) - options.mimeTypes.Add (mimeType.Key, mimeType.Value); - - return options; - } - - /// - /// Registers the subclass for the specified mime-type. - /// - /// The MIME type. - /// A custom subclass of . - /// - /// Your custom class should not subclass - /// directly, but rather it should subclass - /// , , - /// , or one of their derivatives. - /// - /// - /// is null. - /// -or- - /// is null. - /// - /// - /// is not a subclass of , - /// , or . - /// -or- - /// does not have a constructor that takes - /// only a argument. - /// - public void RegisterMimeType (string mimeType, Type type) - { - if (mimeType == null) - throw new ArgumentNullException (nameof (mimeType)); - - if (type == null) - throw new ArgumentNullException (nameof (type)); - - mimeType = mimeType.ToLowerInvariant (); - -#if NETSTANDARD1_3 || NETSTANDARD1_6 - var info = type.GetTypeInfo (); -#else - var info = type; -#endif - - if (!info.IsSubclassOf (typeof (MessagePart)) && - !info.IsSubclassOf (typeof (Multipart)) && - !info.IsSubclassOf (typeof (MimePart))) - throw new ArgumentException ("The specified type must be a subclass of MessagePart, Multipart, or MimePart.", nameof (type)); - - var ctor = type.GetConstructor (ConstructorArgTypes); - - if (ctor == null) - throw new ArgumentException ("The specified type must have a constructor that takes a MimeEntityConstructorArgs argument.", nameof (type)); - - mimeTypes[mimeType] = ctor; - } - - static bool IsEncoded (IList
headers) - { - ContentEncoding encoding; - - for (int i = 0; i < headers.Count; i++) { - if (headers[i].Id != HeaderId.ContentTransferEncoding) - continue; - - MimeUtils.TryParse (headers[i].Value, out encoding); - - switch (encoding) { - case ContentEncoding.SevenBit: - case ContentEncoding.EightBit: - case ContentEncoding.Binary: - return false; - default: - return true; - } - } - - return false; - } - - internal MimeEntity CreateEntity (ContentType contentType, IList
headers, bool toplevel, int depth) - { - var args = new MimeEntityConstructorArgs (this, contentType, headers, toplevel); - - if (depth >= MaxMimeDepth) - return new MimePart (args); - - var subtype = contentType.MediaSubtype.ToLowerInvariant (); - var type = contentType.MediaType.ToLowerInvariant (); - - if (mimeTypes.Count > 0) { - var mimeType = string.Format ("{0}/{1}", type, subtype); - ConstructorInfo ctor; - - if (mimeTypes.TryGetValue (mimeType, out ctor)) - return (MimeEntity) ctor.Invoke (new object[] { args }); - } - - // Note: message/rfc822 and message/partial are not allowed to be encoded according to rfc2046 - // (sections 5.2.1 and 5.2.2, respectively). Since some broken clients will encode them anyway, - // it is necessary for us to treat those as opaque blobs instead, and thus the parser should - // parse them as normal MimeParts instead of MessageParts. - // - // Technically message/disposition-notification is only allowed to have use the 7bit encoding - // as well, but since MessageDispositionNotification is a MImePart subclass rather than a - // MessagePart subclass, it means that the content won't be parsed until later and so we can - // actually handle that w/o any problems. - if (type == "message") { - switch (subtype) { - case "global-disposition-notification": - case "disposition-notification": - return new MessageDispositionNotification (args); - case "global-delivery-status": - case "delivery-status": - return new MessageDeliveryStatus (args); - case "partial": - if (!IsEncoded (headers)) - return new MessagePartial (args); - break; - case "global-headers": - if (!IsEncoded (headers)) - return new TextRfc822Headers (args); - break; - case "external-body": - case "rfc2822": - case "rfc822": - case "global": - case "news": - if (!IsEncoded (headers)) - return new MessagePart (args); - break; - } - } - - if (type == "multipart") { - switch (subtype) { - case "alternative": - return new MultipartAlternative (args); - case "related": - return new MultipartRelated (args); - case "report": - return new MultipartReport (args); -#if ENABLE_CRYPTO - case "encrypted": - return new MultipartEncrypted (args); - case "signed": - return new MultipartSigned (args); -#endif - default: - return new Multipart (args); - } - } - - if (type == "application") { - switch (subtype) { -#if ENABLE_CRYPTO - case "x-pkcs7-signature": - case "pkcs7-signature": - return new ApplicationPkcs7Signature (args); - case "x-pgp-encrypted": - case "pgp-encrypted": - return new ApplicationPgpEncrypted (args); - case "x-pgp-signature": - case "pgp-signature": - return new ApplicationPgpSignature (args); - case "x-pkcs7-mime": - case "pkcs7-mime": - return new ApplicationPkcs7Mime (args); -#endif - case "vnd.ms-tnef": - case "ms-tnef": - return new TnefPart (args); - case "rtf": - return new TextPart (args); - } - } - - if (type == "text") { - if (subtype == "rfc822-headers" && !IsEncoded (headers)) - return new TextRfc822Headers (args); - - return new TextPart (args); - } - - return new MimePart (args); - } - } -} diff --git a/src/MimeKit/Properties/AssemblyInfo.cs b/src/MimeKit/Properties/AssemblyInfo.cs deleted file mode 100644 index f559374..0000000 --- a/src/MimeKit/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// AssemblyInfo.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle ("MimeKit")] -[assembly: AssemblyDescription ("A complete MIME library with support for S/MIME, PGP, DKIM and Unix mbox spools.")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany (".NET Foundation")] -[assembly: AssemblyProduct ("MimeKit")] -[assembly: AssemblyCopyright ("Copyright © 2013-2020 .NET Foundation and Contributors")] -[assembly: AssemblyTrademark (".NET Foundation")] -[assembly: AssemblyCulture ("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible (true)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid ("2fe79b66-d107-45da-9493-175f59c4a53c")] -[assembly: InternalsVisibleTo ("UnitTests, PublicKey=002400000480000094000000060200" + - "00002400005253413100040000110000003fefa5187022727c3471938d10df4c47d5d5ecbe2f36" + - "4656c5bfe4c47803453a91ae525f723f4316fd90a3f87366f4d948593277e950f6d2df6ee26068" + - "1877a6d9e71c3ea77e87e61f3878af1d69bf10dce8debe92c54ca8a10afc44dc08674f3db6594e" + - "f545d67d31cc3e18b8f90d8f220c4b67d7e87f5b7e8df410ac8faeb3")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Micro Version -// Build Number -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -// -// Note: AssemblyVersion is what the CLR matches against at runtime, so be careful -// about updating it. The AssemblyFileVersion is the official release version while -// the AssemblyInformationalVersion is just used as a display version. -// -// Based on my current understanding, AssemblyVersion is essentially the "API Version" -// and so should only be updated when the API changes. The other 2 Version attributes -// represent the "Release Version". -// -// Making releases: -// -// If any classes, methods, or enum values have been added, bump the Micro Version -// in all version attributes and set the Build Number back to 0. -// -// If there have only been bug fixes, bump the Micro Version and/or the Build Number -// in the AssemblyFileVersion attribute. -[assembly: AssemblyInformationalVersion ("2.8.0.0")] -[assembly: AssemblyFileVersion ("2.8.0.0")] -[assembly: AssemblyVersion ("2.8.0.0")] diff --git a/src/MimeKit/RfcComplianceMode.cs b/src/MimeKit/RfcComplianceMode.cs deleted file mode 100644 index 2522f2f..0000000 --- a/src/MimeKit/RfcComplianceMode.cs +++ /dev/null @@ -1,45 +0,0 @@ -// -// RfcComplianceMode.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit { - /// - /// An RFC compliance mode. - /// - /// - /// An RFC compliance mode. - /// - public enum RfcComplianceMode { - /// - /// Attempt to be much more liberal accepting broken and/or invalid formatting. - /// - Loose, - - /// - /// Do not attempt to be overly liberal in accepting broken and/or invalid formatting. - /// - Strict - } -} diff --git a/src/MimeKit/StreamExtensions.cs b/src/MimeKit/StreamExtensions.cs deleted file mode 100644 index c8165fa..0000000 --- a/src/MimeKit/StreamExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// StreamExtensions.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2015 Xamarin Inc. (www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.IO; - -namespace MimeKit { - static class StreamExtensions - { - public static void CopyTo (this Stream source, Stream destination, int bufferSize) - { - var buffer = new byte[bufferSize]; - int nread; - - while ((nread = source.Read (buffer, 0, bufferSize)) > 0) - destination.Write (buffer, 0, nread); - } - - public static void CopyTo (this Stream source, Stream destination) - { - CopyTo (source, destination, 4096); - } - } -} diff --git a/src/MimeKit/Text/CharBuffer.cs b/src/MimeKit/Text/CharBuffer.cs deleted file mode 100644 index 6afdac1..0000000 --- a/src/MimeKit/Text/CharBuffer.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// CharBuffer.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Runtime.CompilerServices; - -namespace MimeKit.Text { - class CharBuffer - { - char[] buffer; - - public CharBuffer (int capacity) - { - buffer = new char[capacity]; - } - - public int Length { - [MethodImpl (MethodImplOptions.AggressiveInlining)] - get; - [MethodImpl (MethodImplOptions.AggressiveInlining)] - set; - } - - public char this[int index] { - [MethodImpl (MethodImplOptions.AggressiveInlining)] - get { return buffer[index]; } - [MethodImpl (MethodImplOptions.AggressiveInlining)] - set { buffer[index] = value; } - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - void EnsureCapacity (int length) - { - if (length < buffer.Length) - return; - - int capacity = buffer.Length << 1; - while (capacity <= length) - capacity <<= 1; - - Array.Resize (ref buffer, capacity); - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - public void Append (char c) - { - EnsureCapacity (Length + 1); - buffer[Length++] = c; - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - public void Append (string str) - { - EnsureCapacity (Length + str.Length); - str.CopyTo (0, buffer, Length, str.Length); - Length += str.Length; - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - public override string ToString () - { - return new string (buffer, 0, Length); - } - - public static implicit operator string (CharBuffer buffer) - { - return buffer.ToString (); - } - } -} diff --git a/src/MimeKit/Text/FlowedToHtml.cs b/src/MimeKit/Text/FlowedToHtml.cs deleted file mode 100644 index 22f77dc..0000000 --- a/src/MimeKit/Text/FlowedToHtml.cs +++ /dev/null @@ -1,423 +0,0 @@ -// -// FlowedToHtml.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; -using System.Collections.Generic; - -namespace MimeKit.Text { - /// - /// A flowed text to HTML converter. - /// - /// - /// Used to convert flowed text (as described in rfc3676) into HTML. - /// - /// - /// - /// - public class FlowedToHtml : TextConverter - { - readonly UrlScanner scanner; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new flowed text to HTML converter. - /// - public FlowedToHtml () - { - scanner = new UrlScanner (); - - for (int i = 0; i < UrlPatterns.Count; i++) - scanner.Add (UrlPatterns[i]); - } - - /// - /// Get the input format. - /// - /// - /// Gets the input format. - /// - /// The input format. - public override TextFormat InputFormat { - get { return TextFormat.Flowed; } - } - - /// - /// Get the output format. - /// - /// - /// Gets the output format. - /// - /// The output format. - public override TextFormat OutputFormat { - get { return TextFormat.Html; } - } - - /// - /// Get or set whether the trailing space on a wrapped line should be deleted. - /// - /// - /// Gets or sets whether the trailing space on a wrapped line should be deleted. - /// The flowed text format defines a Content-Type parameter called "delsp" which can - /// have a value of "yes" or "no". If the parameter exists and the value is "yes", then - /// should be set to true, otherwise - /// should be set to false. - /// - /// - /// - /// - /// true if the trailing space on a wrapped line should be deleted; otherwise, false. - public bool DeleteSpace { - get; set; - } - - /// - /// Get or set the footer format. - /// - /// - /// Gets or sets the footer format. - /// - /// The footer format. - public HeaderFooterFormat FooterFormat { - get; set; - } - - /// - /// Get or set the header format. - /// - /// - /// Gets or sets the header format. - /// - /// The header format. - public HeaderFooterFormat HeaderFormat { - get; set; - } - - /// - /// Get or set the method to use for custom - /// filtering of HTML tags and content. - /// - /// - /// Get or set the method to use for custom - /// filtering of HTML tags and content. - /// - /// The html tag callback. - public HtmlTagCallback HtmlTagCallback { - get; set; - } - - /// - /// Get or set whether or not the converter should only output an HTML fragment. - /// - /// - /// Gets or sets whether or not the converter should only output an entire - /// HTML document or just a fragment of the HTML body content. - /// - /// true if the converter should only output an HTML fragment; otherwise, false. - public bool OutputHtmlFragment { - get; set; - } - - class FlowedToHtmlTagContext : HtmlTagContext - { - HtmlAttributeCollection attrs; - bool isEndTag; - - public FlowedToHtmlTagContext (HtmlTagId tag, HtmlAttribute attr) : base (tag) - { - attrs = new HtmlAttributeCollection (new [] { attr }); - } - - public FlowedToHtmlTagContext (HtmlTagId tag) : base (tag) - { - attrs = HtmlAttributeCollection.Empty; - } - - public override string TagName { - get { return TagId.ToHtmlTagName (); } - } - - public override HtmlAttributeCollection Attributes { - get { return attrs; } - } - - public override bool IsEmptyElementTag { - get { return TagId == HtmlTagId.Br; } - } - - public override bool IsEndTag { - get { return isEndTag; } - } - - public void SetIsEndTag (bool value) - { - isEndTag = value; - } - } - - static void DefaultHtmlTagCallback (HtmlTagContext tagContext, HtmlWriter htmlWriter) - { - tagContext.WriteTag (htmlWriter, true); - } - - static string Unquote (string line, out int quoteDepth) - { - int index = 0; - - quoteDepth = 0; - - if (line.Length == 0) - return line; - - while (index < line.Length && line[index] == '>') { - quoteDepth++; - index++; - } - - if (index > 0 && index < line.Length && line[index] == ' ') - index++; - - return index > 0 ? line.Substring (index) : line; - } - - static bool SuppressContent (IList stack) - { - for (int i = stack.Count; i > 0; i--) { - if (stack[i - 1].SuppressInnerContent) - return true; - } - - return false; - } - - void WriteText (HtmlWriter htmlWriter, string text) - { - var callback = HtmlTagCallback ?? DefaultHtmlTagCallback; - var content = text.ToCharArray (); - int endIndex = content.Length; - int startIndex = 0; - UrlMatch match; - int count; - - do { - count = endIndex - startIndex; - - if (scanner.Scan (content, startIndex, count, out match)) { - count = match.EndIndex - match.StartIndex; - - if (match.StartIndex > startIndex) { - // write everything up to the match - htmlWriter.WriteText (content, startIndex, match.StartIndex - startIndex); - } - - var href = match.Prefix + new string (content, match.StartIndex, count); - var ctx = new FlowedToHtmlTagContext (HtmlTagId.A, new HtmlAttribute (HtmlAttributeId.Href, href)); - callback (ctx, htmlWriter); - - if (!ctx.SuppressInnerContent) - htmlWriter.WriteText (content, match.StartIndex, count); - - if (!ctx.DeleteEndTag) { - ctx.SetIsEndTag (true); - - if (ctx.InvokeCallbackForEndTag) - callback (ctx, htmlWriter); - else - ctx.WriteTag (htmlWriter); - } - - startIndex = match.EndIndex; - } else { - htmlWriter.WriteText (content, startIndex, count); - break; - } - } while (startIndex < endIndex); - } - - void WriteParagraph (HtmlWriter htmlWriter, IList stack, ref int currentQuoteDepth, StringBuilder para, int quoteDepth) - { - var callback = HtmlTagCallback ?? DefaultHtmlTagCallback; - FlowedToHtmlTagContext ctx; - - while (currentQuoteDepth < quoteDepth) { - ctx = new FlowedToHtmlTagContext (HtmlTagId.BlockQuote); - callback (ctx, htmlWriter); - currentQuoteDepth++; - stack.Add (ctx); - } - - while (quoteDepth < currentQuoteDepth) { - ctx = stack[stack.Count - 1]; - stack.RemoveAt (stack.Count - 1); - - if (!SuppressContent (stack) && !ctx.DeleteEndTag) { - ctx.SetIsEndTag (true); - - if (ctx.InvokeCallbackForEndTag) - callback (ctx, htmlWriter); - else - ctx.WriteTag (htmlWriter); - } - - if (ctx.TagId == HtmlTagId.BlockQuote) - currentQuoteDepth--; - } - - if (SuppressContent (stack)) - return; - - ctx = new FlowedToHtmlTagContext (para.Length == 0 ? HtmlTagId.Br : HtmlTagId.P); - callback (ctx, htmlWriter); - - if (para.Length > 0) { - if (!ctx.SuppressInnerContent) - WriteText (htmlWriter, para.ToString ()); - - if (!ctx.DeleteEndTag) { - ctx.SetIsEndTag (true); - - if (ctx.InvokeCallbackForEndTag) - callback (ctx, htmlWriter); - else - ctx.WriteTag (htmlWriter); - } - } - - if (!ctx.DeleteTag) - htmlWriter.WriteMarkupText (Environment.NewLine); - } - - /// - /// Convert the contents of from the to the - /// and uses the to write the resulting text. - /// - /// - /// Converts the contents of from the to the - /// and uses the to write the resulting text. - /// - /// The text reader. - /// The text writer. - /// - /// is null. - /// -or- - /// is null. - /// - public override void Convert (TextReader reader, TextWriter writer) - { - if (reader == null) - throw new ArgumentNullException (nameof (reader)); - - if (writer == null) - throw new ArgumentNullException (nameof (writer)); - - if (!OutputHtmlFragment) - writer.Write (""); - - if (!string.IsNullOrEmpty (Header)) { - if (HeaderFormat == HeaderFooterFormat.Text) { - var converter = new TextToHtml { OutputHtmlFragment = true }; - - using (var sr = new StringReader (Header)) - converter.Convert (sr, writer); - } else { - writer.Write (Header); - } - } - - using (var htmlWriter = new HtmlWriter (writer)) { - var callback = HtmlTagCallback ?? DefaultHtmlTagCallback; - var stack = new List (); - var para = new StringBuilder (); - int currentQuoteDepth = 0; - int paraQuoteDepth = -1; - int quoteDepth; - string line; - - while ((line = reader.ReadLine ()) != null) { - // unquote the line - line = Unquote (line, out quoteDepth); - - // remove space-stuffing - if (line.Length > 0 && line[0] == ' ') - line = line.Substring (1); - - if (para.Length == 0) { - paraQuoteDepth = quoteDepth; - } else if (quoteDepth != paraQuoteDepth) { - // Note: according to rfc3676, when a folded line has a different quote - // depth than the previous line, then quote-depth rules win and we need - // to treat this as a new paragraph. - WriteParagraph (htmlWriter, stack, ref currentQuoteDepth, para, paraQuoteDepth); - paraQuoteDepth = quoteDepth; - para.Length = 0; - } - - para.Append (line); - - if (line.Length == 0 || line[line.Length - 1] != ' ') { - // line did not end with a space, so the next line will start a new paragraph - WriteParagraph (htmlWriter, stack, ref currentQuoteDepth, para, paraQuoteDepth); - paraQuoteDepth = 0; - para.Length = 0; - } else if (DeleteSpace) { - // Note: lines ending with a space mean that the next line is a continuation - para.Length--; - } - } - - for (int i = stack.Count; i > 0; i--) { - var ctx = stack[i - 1]; - - ctx.SetIsEndTag (true); - - if (ctx.InvokeCallbackForEndTag) - callback (ctx, htmlWriter); - else - ctx.WriteTag (htmlWriter); - } - - htmlWriter.Flush (); - } - - if (!string.IsNullOrEmpty (Footer)) { - if (FooterFormat == HeaderFooterFormat.Text) { - var converter = new TextToHtml { OutputHtmlFragment = true }; - - using (var sr = new StringReader (Footer)) - converter.Convert (sr, writer); - } else { - writer.Write (Footer); - } - } - - if (!OutputHtmlFragment) - writer.Write (""); - } - } -} diff --git a/src/MimeKit/Text/FlowedToText.cs b/src/MimeKit/Text/FlowedToText.cs deleted file mode 100644 index 3b0092f..0000000 --- a/src/MimeKit/Text/FlowedToText.cs +++ /dev/null @@ -1,177 +0,0 @@ -// -// FlowedToText.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Text; - -namespace MimeKit.Text { - /// - /// A flowed text to text converter. - /// - /// - /// Unwraps the flowed text format described in rfc3676. - /// - public class FlowedToText : TextConverter - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new flowed text to text converter. - /// - public FlowedToText () - { - } - - /// - /// Get the input format. - /// - /// - /// Gets the input format. - /// - /// The input format. - public override TextFormat InputFormat { - get { return TextFormat.Flowed; } - } - - /// - /// Get the output format. - /// - /// - /// Gets the output format. - /// - /// The output format. - public override TextFormat OutputFormat { - get { return TextFormat.Plain; } - } - - /// - /// Get or set whether the trailing space on a wrapped line should be deleted. - /// - /// - /// Gets or sets whether the trailing space on a wrapped line should be deleted. - /// The flowed text format defines a Content-Type parameter called "delsp" which can - /// have a value of "yes" or "no". If the parameter exists and the value is "yes", then - /// should be set to true, otherwise - /// should be set to false. - /// - /// true if the trailing space on a wrapped line should be deleted; otherwise, false. - public bool DeleteSpace { - get; set; - } - - static string Unquote (string line, out int quoteDepth) - { - int index = 0; - - quoteDepth = 0; - - if (line.Length == 0) - return line; - - while (index < line.Length && line[index] == '>') { - quoteDepth++; - index++; - } - - if (index > 0 && index < line.Length && line[index] == ' ') - index++; - - return index > 0 ? line.Substring (index) : line; - } - - /// - /// Convert the contents of from the to the - /// and uses the to write the resulting text. - /// - /// - /// Converts the contents of from the to the - /// and uses the to write the resulting text. - /// - /// The text reader. - /// The text writer. - /// - /// is null. - /// -or- - /// is null. - /// - public override void Convert (TextReader reader, TextWriter writer) - { - StringBuilder para = new StringBuilder (); - int paraQuoteDepth = -1; - int quoteDepth; - string line; - - if (reader == null) - throw new ArgumentNullException (nameof (reader)); - - if (writer == null) - throw new ArgumentNullException (nameof (writer)); - - if (!string.IsNullOrEmpty (Header)) - writer.Write (Header); - - while ((line = reader.ReadLine ()) != null) { - line = Unquote (line, out quoteDepth); - - // if there is a leading space, it was stuffed - if (quoteDepth == 0 && line.Length > 0 && line[0] == ' ') - line = line.Substring (1); - - if (paraQuoteDepth == -1) { - paraQuoteDepth = quoteDepth; - } else if (quoteDepth != paraQuoteDepth) { - // Note: according to rfc3676, when a folded line has a different quote - // depth than the previous line, then quote-depth rules win and we need - // to treat this as a new paragraph. - if (paraQuoteDepth > 0) - writer.Write (new string ('>', paraQuoteDepth) + " "); - writer.WriteLine (para); - paraQuoteDepth = quoteDepth; - para.Length = 0; - } - - para.Append (line); - - if (line.Length == 0 || line[line.Length - 1] != ' ') { - // when a line does not end with a space, then the paragraph has ended - if (paraQuoteDepth > 0) - writer.Write (new string ('>', paraQuoteDepth) + " "); - writer.WriteLine (para); - paraQuoteDepth = -1; - para.Length = 0; - } else if (DeleteSpace) { - // Note: lines ending with a space mean that the next line is a continuation - para.Length--; - } - } - - if (!string.IsNullOrEmpty (Footer)) - writer.Write (Footer); - } - } -} diff --git a/src/MimeKit/Text/HeaderFooterFormat.cs b/src/MimeKit/Text/HeaderFooterFormat.cs deleted file mode 100644 index a5bd0eb..0000000 --- a/src/MimeKit/Text/HeaderFooterFormat.cs +++ /dev/null @@ -1,45 +0,0 @@ -// -// HeaderFooterFormat.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Text { - /// - /// An enumeration of possible header and footer formats. - /// - /// - /// An enumeration of possible header and footer formats. - /// - public enum HeaderFooterFormat { - /// - /// The header or footer contains plain text. - /// - Text, - - /// - /// The header or footer contains properly formatted HTML. - /// - Html - } -} diff --git a/src/MimeKit/Text/HtmlAttribute.cs b/src/MimeKit/Text/HtmlAttribute.cs deleted file mode 100644 index 3586c59..0000000 --- a/src/MimeKit/Text/HtmlAttribute.cs +++ /dev/null @@ -1,143 +0,0 @@ -// -// HtmlAttribute.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Text { - /// - /// An HTML attribute. - /// - /// - /// An HTML attribute. - /// - /// - /// - /// - public class HtmlAttribute - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new HTML attribute with the given id and value. - /// - /// The attribute identifier. - /// The attribute value. - /// - /// is not a valid value. - /// - public HtmlAttribute (HtmlAttributeId id, string value) - { - if (id == HtmlAttributeId.Unknown) - throw new ArgumentOutOfRangeException (nameof (id)); - - Name = id.ToAttributeName (); - Value = value; - Id = id; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new HTML attribute with the given name and value. - /// - /// The attribute name. - /// The attribute value. - /// - /// is null. - /// - public HtmlAttribute (string name, string value) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (name.Length == 0) - throw new ArgumentException ("The attribute name cannot be empty.", nameof (name)); - - if (!HtmlUtils.IsValidTokenName (name)) - throw new ArgumentException ("Invalid attribute name.", nameof (name)); - - Id = name.ToHtmlAttributeId (); - Value = value; - Name = name; - } - - internal HtmlAttribute (string name) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (name.Length == 0) - throw new ArgumentException ("The attribute name cannot be empty.", nameof (name)); - - Id = name.ToHtmlAttributeId (); - Name = name; - } - - /// - /// Get the HTML attribute identifier. - /// - /// - /// Gets the HTML attribute identifier. - /// - /// - /// - /// - /// The attribute identifier. - public HtmlAttributeId Id { - get; private set; - } - - /// - /// Get the name of the attribute. - /// - /// - /// Gets the name of the attribute. - /// - /// - /// - /// - /// The name of the attribute. - public string Name { - get; private set; - } - - /// - /// Get the value of the attribute. - /// - /// - /// Gets the value of the attribute. - /// - /// - /// - /// - /// The value of the attribute. - public string Value { - get; internal set; - } - } -} diff --git a/src/MimeKit/Text/HtmlAttributeCollection.cs b/src/MimeKit/Text/HtmlAttributeCollection.cs deleted file mode 100644 index e805c2f..0000000 --- a/src/MimeKit/Text/HtmlAttributeCollection.cs +++ /dev/null @@ -1,125 +0,0 @@ -// -// HtmlAttributeCollection.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace MimeKit.Text { - /// - /// A readonly collection of HTML attributes. - /// - /// - /// A readonly collection of HTML attributes. - /// - public class HtmlAttributeCollection : IEnumerable - { - /// - /// An empty attribute collection. - /// - /// - /// An empty attribute collection. - /// - public static readonly HtmlAttributeCollection Empty = new HtmlAttributeCollection (); - - readonly List attributes = new List (); - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// A collection of attributes. - public HtmlAttributeCollection (IEnumerable collection) - { - attributes = new List (collection); - } - - internal HtmlAttributeCollection () - { - attributes = new List (); - } - - /// - /// Get the number of attributes in the collection. - /// - /// - /// Gets the number of attributes in the collection. - /// - /// The number of attributes in the collection. - public int Count { - get { return attributes.Count; } - } - - internal void Add (HtmlAttribute attribute) - { - if (attribute == null) - throw new ArgumentNullException (nameof (attribute)); - - attributes.Add (attribute); - } - - /// - /// Get the at the specified index. - /// - /// - /// Gets the at the specified index. - /// - /// The HTML attribute at the specified index. - /// The index. - /// - /// is out of range. - /// - public HtmlAttribute this[int index] { - get { return attributes[index]; } - } - - /// - /// Gets an enumerator for the attribute collection. - /// - /// - /// Gets an enumerator for the attribute collection. - /// - /// The enumerator. - public IEnumerator GetEnumerator () - { - return attributes.GetEnumerator (); - } - - /// - /// Gets an enumerator for the attribute collection. - /// - /// - /// Gets an enumerator for the attribute collection. - /// - /// The enumerator. - IEnumerator IEnumerable.GetEnumerator () - { - return attributes.GetEnumerator (); - } - } -} diff --git a/src/MimeKit/Text/HtmlAttributeId.cs b/src/MimeKit/Text/HtmlAttributeId.cs deleted file mode 100644 index 7c61ead..0000000 --- a/src/MimeKit/Text/HtmlAttributeId.cs +++ /dev/null @@ -1,656 +0,0 @@ -// -// HtmlAttributeId.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Reflection; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit.Text { - /// - /// HTML attribute identifiers. - /// - /// - /// HTML attribute identifiers. - /// - public enum HtmlAttributeId { - /// - /// An unknown HTML attribute identifier. - /// - Unknown, - - /// - /// The "abbr" attribute. - /// - Abbr, - - /// - /// The "accept" attribute. - /// - Accept, - - /// - /// The "accept-charset" attribute. - /// - [HtmlAttributeName ("accept-charset")] - AcceptCharset, - - /// - /// The "accesskey" attribute. - /// - AccessKey, - - /// - /// The "action" attribute. - /// - Action, - - /// - /// The "align" attribute. - /// - Align, - - /// - /// The "alink" attribute. - /// - Alink, - - /// - /// The "alt" attribute. - /// - Alt, - - /// - /// The "archive" attribute. - /// - Archive, - - /// - /// The "axis" attribute. - /// - Axis, - - /// - /// The "background" attribute. - /// - Background, - - /// - /// The "bgcolor" attribute. - /// - BGColor, - - /// - /// The "border" attribute. - /// - Border, - - /// - /// The "cellpadding" attribute. - /// - CellPadding, - - /// - /// The "cellspacing" attribute. - /// - CellSpacing, - - /// - /// The "char" attribute. - /// - Char, - - /// - /// The "charoff" attribute. - /// - CharOff, - - /// - /// The "charset" attribute. - /// - Charset, - - /// - /// The "checked" attribute. - /// - Checked, - - /// - /// The "cite" attribute. - /// - Cite, - - /// - /// The "class" attribute. - /// - Class, - - /// - /// The "classid" attribute. - /// - ClassId, - - /// - /// The "clear" attribute. - /// - Clear, - - /// - /// The "code" attribute. - /// - Code, - - /// - /// The "codebase" attribute. - /// - CodeBase, - - /// - /// The "codetype" attribute. - /// - CodeType, - - /// - /// The "color" attribute. - /// - Color, - - /// - /// The "cols" attribute. - /// - Cols, - - /// - /// The "colspan" attribute. - /// - ColSpan, - - /// - /// The "compact" attribute. - /// - Compact, - - /// - /// The "content" attribute. - /// - Content, - - /// - /// The "coords" attribute. - /// - Coords, - - /// - /// The "data" attribute. - /// - Data, - - /// - /// The "datetime" attribute. - /// - DateTime, - - /// - /// The "declare" attribute. - /// - Declare, - - /// - /// The "defer" attribute. - /// - Defer, - - /// - /// The "dir" attribute. - /// - Dir, - - /// - /// The "disabled" attribute. - /// - Disabled, - - /// - /// The "dynsrc" attribute. - /// - DynSrc, - - /// - /// The "enctype" attribute. - /// - EncType, - - /// - /// The "face" attribute. - /// - Face, - - /// - /// The "for" attribute. - /// - For, - - /// - /// The "frame" attribute. - /// - Frame, - - /// - /// The "frameborder" attribute. - /// - FrameBorder, - - /// - /// The "headers" attribute. - /// - Headers, - - /// - /// The "height" attribute. - /// - Height, - - /// - /// The "href" attribute. - /// - Href, - - /// - /// The "hreflang" attribute. - /// - HrefLang, - - /// - /// The "hspace" attribute. - /// - Hspace, - - /// - /// The "http-equiv" attribute. - /// - [HtmlAttributeName ("http-equiv")] - HttpEquiv, - - /// - /// The "id" attribute. - /// - Id, - - /// - /// The "ismap" attribute. - /// - IsMap, - - /// - /// The "label" attribute. - /// - Label, - - /// - /// The "lang" attribute. - /// - Lang, - - /// - /// The "language" attribute. - /// - Language, - - /// - /// The "leftmargin" attribute. - /// - LeftMargin, - - /// - /// The "link" attribute. - /// - Link, - - /// - /// The "longdesc" attribute. - /// - LongDesc, - - /// - /// The "lowsrc" attribute. - /// - LowSrc, - - /// - /// The "marginheight" attribute. - /// - MarginHeight, - - /// - /// The "marginwidth" attribute. - /// - MarginWidth, - - /// - /// The "maxlength" attribute. - /// - MaxLength, - - /// - /// The "media" attribute. - /// - Media, - - /// - /// The "method" attribute. - /// - Method, - - /// - /// The "multiple" attribute. - /// - Multiple, - - /// - /// The "name" attribute. - /// - Name, - - /// - /// The "nohref" attribute. - /// - NoHref, - - /// - /// The "noresize" attribute. - /// - NoResize, - - /// - /// The "noshade" attribute. - /// - NoShade, - - /// - /// The "nowrap" attribute. - /// - NoWrap, - - /// - /// The "object" attribute. - /// - Object, - - /// - /// The "profile" attribute. - /// - Profile, - - /// - /// The "prompt" attribute. - /// - Prompt, - - /// - /// The "readonly" attribute. - /// - ReadOnly, - - /// - /// The "rel" attribute. - /// - Rel, - - /// - /// The "rev" attribute. - /// - Rev, - - /// - /// The "rows" attribute. - /// - Rows, - - /// - /// The "rowspan" attribute. - /// - RowSpan, - - /// - /// The "rules" attribute. - /// - Rules, - - /// - /// The "scheme" attribute. - /// - Scheme, - - /// - /// The "scope" attribute. - /// - Scope, - - /// - /// The "scrolling" attribute. - /// - Scrolling, - - /// - /// The "selected" attribute. - /// - Selected, - - /// - /// The "shape" attribute. - /// - Shape, - - /// - /// The "size" attribute. - /// - Size, - - /// - /// The "span" attribute. - /// - Span, - - /// - /// The "src" attribute. - /// - Src, - - /// - /// The "standby" attribute. - /// - StandBy, - - /// - /// The "start" attribute. - /// - Start, - - /// - /// The "style" attribute. - /// - Style, - - /// - /// The "summary" attribute. - /// - Summary, - - /// - /// The "tabindex" attribute. - /// - TabIndex, - - /// - /// The "target" attribute. - /// - Target, - - /// - /// The "text" attribute. - /// - Text, - - /// - /// The "title" attribute. - /// - Title, - - /// - /// The "topmargin" attribute. - /// - TopMargin, - - /// - /// The "type" attribute. - /// - Type, - - /// - /// The "usemap" attribute. - /// - UseMap, - - /// - /// The "valign" attribute. - /// - Valign, - - /// - /// The "value" attribute. - /// - Value, - - /// - /// The "valuetype" attribute. - /// - ValueType, - - /// - /// The "version" attribute. - /// - Version, - - /// - /// The "vlink" attribute. - /// - Vlink, - - /// - /// The "vspace" attribute. - /// - Vspace, - - /// - /// The "width" attribute. - /// - Width, - - /// - /// The "xmlns" attribute. - /// - XmlNS - } - - [AttributeUsage (AttributeTargets.Field)] - class HtmlAttributeNameAttribute : Attribute { - public HtmlAttributeNameAttribute (string name) - { - Name = name; - } - - public string Name { - get; protected set; - } - } - - /// - /// extension methods. - /// - /// - /// extension methods. - /// - public static class HtmlAttributeIdExtensions - { - static readonly Dictionary AttributeNameToId; - - static HtmlAttributeIdExtensions () - { - var values = (HtmlAttributeId[]) Enum.GetValues (typeof (HtmlAttributeId)); - - AttributeNameToId = new Dictionary (values.Length - 1, MimeUtils.OrdinalIgnoreCase); - - for (int i = 1; i < values.Length; i++) - AttributeNameToId.Add (values[i].ToAttributeName (), values[i]); - } - - /// - /// Converts the enum value into the equivalent attribute name. - /// - /// - /// Converts the enum value into the equivalent attribute name. - /// - /// The attribute name. - /// The enum value. - public static string ToAttributeName (this HtmlAttributeId value) - { - var name = value.ToString (); - -#if NETSTANDARD1_3 || NETSTANDARD1_6 - var field = typeof (HtmlAttributeId).GetTypeInfo ().GetDeclaredField (name); - var attrs = field.GetCustomAttributes (typeof (HtmlAttributeNameAttribute), false).ToArray (); -#else - var field = typeof (HtmlAttributeId).GetField (name); - var attrs = field.GetCustomAttributes (typeof (HtmlAttributeNameAttribute), false); -#endif - - if (attrs != null && attrs.Length == 1) - return ((HtmlAttributeNameAttribute) attrs[0]).Name; - - return name.ToLowerInvariant (); - } - - /// - /// Converts the attribute name into the equivalent attribute id. - /// - /// - /// Converts the attribute name into the equivalent attribute id. - /// - /// The attribute id. - /// The attribute name. - internal static HtmlAttributeId ToHtmlAttributeId (this string name) - { - HtmlAttributeId value; - - if (!AttributeNameToId.TryGetValue (name, out value)) - return HtmlAttributeId.Unknown; - - return value; - } - } -} diff --git a/src/MimeKit/Text/HtmlEntityDecoder.cs b/src/MimeKit/Text/HtmlEntityDecoder.cs deleted file mode 100644 index 3838bdc..0000000 --- a/src/MimeKit/Text/HtmlEntityDecoder.cs +++ /dev/null @@ -1,258 +0,0 @@ -// -// HtmlEntityDecoder.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Text { - /// - /// An HTML entity decoder. - /// - /// - /// An HTML entity decoder. - /// - public partial class HtmlEntityDecoder - { - readonly char[] pushed; - readonly int[] states; - bool semicolon; - bool numeric; - byte digits; - byte xbase; - int index; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public HtmlEntityDecoder () - { - pushed = new char[MaxEntityLength]; - states = new int[MaxEntityLength]; - } - - bool PushNumericEntity (char c) - { - int v; - - if (xbase == 0) { - if (c == 'X' || c == 'x') { - states[index] = 0; - pushed[index] = c; - xbase = 16; - index++; - return true; - } - - xbase = 10; - } - - if (c <= '9') { - if (c < '0') - return false; - - v = c - '0'; - } else if (xbase == 16) { - if (c >= 'a') { - v = (c - 'a') + 10; - } else if (c >= 'A') { - v = (c - 'A') + 10; - } else { - return false; - } - } else { - return false; - } - - if (v >= (int) xbase) - return false; - - int state = states[index - 1]; - - // check for overflow - if (state > int.MaxValue / xbase) - return false; - - if (state == int.MaxValue / xbase && v > int.MaxValue % xbase) - return false; - - state = (state * xbase) + v; - states[index] = state; - pushed[index] = c; - digits++; - index++; - - return true; - } - - /// - /// Push the specified character into the HTML entity decoder. - /// - /// - /// Pushes the specified character into the HTML entity decoder. - /// The first character pushed MUST be the '&' character. - /// - /// true if the character was accepted; otherwise, false. - /// The character. - /// - /// is the first character being pushed and was not the '&' character. - /// - public bool Push (char c) - { - if (semicolon) - return false; - - if (index == 0) { - if (c != '&') - throw new ArgumentOutOfRangeException (nameof (c), "The first character that is pushed MUST be the '&' character."); - - pushed[index] = '&'; - states[index] = 0; - index++; - return true; - } - - if (index + 1 > MaxEntityLength) - return false; - - if (index == 1 && c == '#') { - pushed[index] = '#'; - states[index] = 0; - numeric = true; - index++; - return true; - } - - semicolon = c == ';'; - - if (numeric) { - if (c == ';') { - states[index] = states[index - 1]; - pushed[index] = ';'; - index++; - return true; - } - - return PushNumericEntity (c); - } - - return PushNamedEntity (c); - } - - string GetNumericEntityValue () - { - if (digits == 0 || !semicolon) - return new string (pushed, 0, index); - - int state = states[index - 1]; - - // the following states are parse errors - switch (state) { - case 0x00: return "\uFFFD"; // REPLACEMENT CHARACTER - case 0x80: return "\u20AC"; // EURO SIGN (€) - case 0x82: return "\u201A"; // SINGLE LOW-9 QUOTATION MARK (‚) - case 0x83: return "\u0192"; // LATIN SMALL LETTER F WITH HOOK (ƒ) - case 0x84: return "\u201E"; // DOUBLE LOW-9 QUOTATION MARK („) - case 0x85: return "\u2026"; // HORIZONTAL ELLIPSIS (…) - case 0x86: return "\u2020"; // DAGGER (†) - case 0x87: return "\u2021"; // DOUBLE DAGGER (‡) - case 0x88: return "\u02C6"; // MODIFIER LETTER CIRCUMFLEX ACCENT (ˆ) - case 0x89: return "\u2030"; // PER MILLE SIGN (‰) - case 0x8A: return "\u0160"; // LATIN CAPITAL LETTER S WITH CARON (Š) - case 0x8B: return "\u2039"; // SINGLE LEFT-POINTING ANGLE QUOTATION MARK (‹) - case 0x8C: return "\u0152"; // LATIN CAPITAL LIGATURE OE (Œ) - case 0x8E: return "\u017D"; // LATIN CAPITAL LETTER Z WITH CARON (Ž) - case 0x91: return "\u2018"; // LEFT SINGLE QUOTATION MARK (‘) - case 0x92: return "\u2019"; // RIGHT SINGLE QUOTATION MARK (’) - case 0x93: return "\u201C"; // LEFT DOUBLE QUOTATION MARK (“) - case 0x94: return "\u201D"; // RIGHT DOUBLE QUOTATION MARK (”) - case 0x95: return "\u2022"; // BULLET (•) - case 0x96: return "\u2013"; // EN DASH (–) - case 0x97: return "\u2014"; // EM DASH (—) - case 0x98: return "\u02DC"; // SMALL TILDE (˜) - case 0x99: return "\u2122"; // TRADE MARK SIGN (™) - case 0x9A: return "\u0161"; // LATIN SMALL LETTER S WITH CARON (š) - case 0x9B: return "\u203A"; // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (›) - case 0x9C: return "\u0153"; // LATIN SMALL LIGATURE OE (œ) - case 0x9E: return "\u017E"; // LATIN SMALL LETTER Z WITH CARON (ž) - case 0x9F: return "\u0178"; // LATIN CAPITAL LETTER Y WITH DIAERESIS (Ÿ) - case 0x0000B: case 0x0FFFE: case 0x1FFFE: case 0x1FFFF: case 0x2FFFE: case 0x2FFFF: case 0x3FFFE: - case 0x3FFFF: case 0x4FFFE: case 0x4FFFF: case 0x5FFFE: case 0x5FFFF: case 0x6FFFE: case 0x6FFFF: - case 0x7FFFE: case 0x7FFFF: case 0x8FFFE: case 0x8FFFF: case 0x9FFFE: case 0x9FFFF: case 0xAFFFE: - case 0xAFFFF: case 0xBFFFE: case 0xBFFFF: case 0xCFFFE: case 0xCFFFF: case 0xDFFFE: case 0xDFFFF: - case 0xEFFFE: case 0xEFFFF: case 0xFFFFE: case 0xFFFFF: case 0x10FFFE: case 0x10FFFF: - // parse error - return new string (pushed, 0, index); - default: - if ((state >= 0xD800 && state <= 0xDFFF) || state > 0x10FFFF) { - // parse error, emit REPLACEMENT CHARACTER - return "\uFFFD"; - } - - if ((state >= 0x0001 && state <= 0x0008) || (state >= 0x000D && state <= 0x001F) || - (state >= 0x007F && state <= 0x009F) || (state >= 0xFDD0 && state <= 0xFDEF)) { - return new string (pushed, 0, index); - } - break; - } - - return char.ConvertFromUtf32 (state); - } - - /// - /// Get the decoded entity value. - /// - /// - /// Gets the decoded entity value. - /// - /// The value. - public string GetValue () - { - return numeric ? GetNumericEntityValue () : GetNamedEntityValue (); - } - - internal string GetPushedInput () - { - return new string (pushed, 0, index); - } - - /// - /// Reset the entity decoder. - /// - /// - /// Resets the entity decoder. - /// - public void Reset () - { - semicolon = false; - numeric = false; - digits = 0; - xbase = 0; - index = 0; - } - } -} diff --git a/src/MimeKit/Text/HtmlEntityDecoder.g.cs b/src/MimeKit/Text/HtmlEntityDecoder.g.cs deleted file mode 100644 index f9cc9c9..0000000 --- a/src/MimeKit/Text/HtmlEntityDecoder.g.cs +++ /dev/null @@ -1,12445 +0,0 @@ -// -// HtmlEntityDecoder.g.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// WARNING: This file is auto-generated. DO NOT EDIT! - -using System.Collections.Generic; - -namespace MimeKit.Text { - public partial class HtmlEntityDecoder - { - const int MaxEntityLength = 33; - - static readonly Dictionary NamedEntities; - - struct Transition - { - public readonly int From; - public readonly int To; - - public Transition (int from, int to) - { - From = from; - To = to; - } - } - - static readonly Transition[] TransitionTable_1; - static readonly Transition[] TransitionTable_2; - static readonly Transition[] TransitionTable_3; - static readonly Transition[] TransitionTable_4; - static readonly Transition[] TransitionTable_5; - static readonly Transition[] TransitionTable_6; - static readonly Transition[] TransitionTable_7; - static readonly Transition[] TransitionTable_8; - static readonly Transition[] TransitionTable_semicolon; - static readonly Transition[] TransitionTable_A; - static readonly Transition[] TransitionTable_B; - static readonly Transition[] TransitionTable_C; - static readonly Transition[] TransitionTable_D; - static readonly Transition[] TransitionTable_E; - static readonly Transition[] TransitionTable_F; - static readonly Transition[] TransitionTable_G; - static readonly Transition[] TransitionTable_H; - static readonly Transition[] TransitionTable_I; - static readonly Transition[] TransitionTable_J; - static readonly Transition[] TransitionTable_K; - static readonly Transition[] TransitionTable_L; - static readonly Transition[] TransitionTable_M; - static readonly Transition[] TransitionTable_N; - static readonly Transition[] TransitionTable_O; - static readonly Transition[] TransitionTable_P; - static readonly Transition[] TransitionTable_Q; - static readonly Transition[] TransitionTable_R; - static readonly Transition[] TransitionTable_S; - static readonly Transition[] TransitionTable_T; - static readonly Transition[] TransitionTable_U; - static readonly Transition[] TransitionTable_V; - static readonly Transition[] TransitionTable_W; - static readonly Transition[] TransitionTable_X; - static readonly Transition[] TransitionTable_Y; - static readonly Transition[] TransitionTable_Z; - static readonly Transition[] TransitionTable_a; - static readonly Transition[] TransitionTable_b; - static readonly Transition[] TransitionTable_c; - static readonly Transition[] TransitionTable_d; - static readonly Transition[] TransitionTable_e; - static readonly Transition[] TransitionTable_f; - static readonly Transition[] TransitionTable_g; - static readonly Transition[] TransitionTable_h; - static readonly Transition[] TransitionTable_i; - static readonly Transition[] TransitionTable_j; - static readonly Transition[] TransitionTable_k; - static readonly Transition[] TransitionTable_l; - static readonly Transition[] TransitionTable_m; - static readonly Transition[] TransitionTable_n; - static readonly Transition[] TransitionTable_o; - static readonly Transition[] TransitionTable_p; - static readonly Transition[] TransitionTable_q; - static readonly Transition[] TransitionTable_r; - static readonly Transition[] TransitionTable_s; - static readonly Transition[] TransitionTable_t; - static readonly Transition[] TransitionTable_u; - static readonly Transition[] TransitionTable_v; - static readonly Transition[] TransitionTable_w; - static readonly Transition[] TransitionTable_x; - static readonly Transition[] TransitionTable_y; - static readonly Transition[] TransitionTable_z; - - static HtmlEntityDecoder () - { - TransitionTable_1 = new Transition[4] { - new Transition (566, 567), // &blk -> &blk1 - new Transition (2280, 2282), // &emsp -> &emsp1 - new Transition (2649, 2650), // &frac -> &frac1 - new Transition (8284, 8286) // &sup -> ¹ - }; - TransitionTable_2 = new Transition[4] { - new Transition (567, 568), // &blk1 -> &blk12 - new Transition (2649, 2663), // &frac -> &frac2 - new Transition (2650, 2651), // &frac1 -> ½ - new Transition (8284, 8288) // &sup -> ² - }; - TransitionTable_3 = new Transition[6] { - new Transition (566, 572), // &blk -> &blk3 - new Transition (2282, 2283), // &emsp1 -> &emsp13 - new Transition (2649, 2668), // &frac -> &frac3 - new Transition (2650, 2653), // &frac1 -> &frac13 - new Transition (2663, 2664), // &frac2 -> &frac23 - new Transition (8284, 8290) // &sup -> ³ - }; - TransitionTable_4 = new Transition[7] { - new Transition (567, 570), // &blk1 -> &blk14 - new Transition (572, 573), // &blk3 -> &blk34 - new Transition (2282, 2285), // &emsp1 -> &emsp14 - new Transition (2649, 2675), // &frac -> &frac4 - new Transition (2650, 2655), // &frac1 -> ¼ - new Transition (2668, 2669), // &frac3 -> ¾ - new Transition (8464, 8465) // &there -> &there4 - }; - TransitionTable_5 = new Transition[5] { - new Transition (2649, 2678), // &frac -> &frac5 - new Transition (2650, 2657), // &frac1 -> &frac15 - new Transition (2663, 2666), // &frac2 -> &frac25 - new Transition (2668, 2671), // &frac3 -> &frac35 - new Transition (2675, 2676) // &frac4 -> &frac45 - }; - TransitionTable_6 = new Transition[2] { - new Transition (2650, 2659), // &frac1 -> &frac16 - new Transition (2678, 2679) // &frac5 -> &frac56 - }; - TransitionTable_7 = new Transition[1] { - new Transition (2649, 2683) // &frac -> &frac7 - }; - TransitionTable_8 = new Transition[4] { - new Transition (2650, 2661), // &frac1 -> &frac18 - new Transition (2668, 2673), // &frac3 -> &frac38 - new Transition (2678, 2681), // &frac5 -> &frac58 - new Transition (2683, 2684) // &frac7 -> &frac78 - }; - TransitionTable_semicolon = new Transition[2125] { - new Transition (6, 7), // Á -> Á - new Transition (13, 14), // á -> á - new Transition (19, 20), // &Abreve -> Ă - new Transition (25, 26), // &abreve -> ă - new Transition (27, 28), // &ac -> ∾ - new Transition (29, 30), // &acd -> ∿ - new Transition (31, 32), // &acE -> ∾̳ - new Transition (36, 37), //  ->  - new Transition (40, 41), // â -> â - new Transition (44, 45), // ´ -> ´ - new Transition (46, 47), // &Acy -> А - new Transition (48, 49), // &acy -> а - new Transition (53, 54), // Æ -> Æ - new Transition (58, 59), // æ -> æ - new Transition (60, 61), // &af -> ⁡ - new Transition (63, 64), // &Afr -> 𝔄 - new Transition (65, 66), // &afr -> 𝔞 - new Transition (71, 72), // À -> À - new Transition (77, 78), // à -> à - new Transition (84, 85), // &alefsym -> ℵ - new Transition (87, 88), // &aleph -> ℵ - new Transition (92, 93), // &Alpha -> Α - new Transition (96, 97), // &alpha -> α - new Transition (101, 102), // &Amacr -> Ā - new Transition (106, 107), // &amacr -> ā - new Transition (109, 110), // &amalg -> ⨿ - new Transition (112, 113), // & -> & - new Transition (114, 115), // & -> & - new Transition (117, 118), // &And -> ⩓ - new Transition (120, 121), // &and -> ∧ - new Transition (124, 125), // &andand -> ⩕ - new Transition (126, 127), // &andd -> ⩜ - new Transition (132, 133), // &andslope -> ⩘ - new Transition (134, 135), // &andv -> ⩚ - new Transition (136, 137), // &ang -> ∠ - new Transition (138, 139), // &ange -> ⦤ - new Transition (141, 142), // &angle -> ∠ - new Transition (145, 146), // &angmsd -> ∡ - new Transition (148, 149), // &angmsdaa -> ⦨ - new Transition (150, 151), // &angmsdab -> ⦩ - new Transition (152, 153), // &angmsdac -> ⦪ - new Transition (154, 155), // &angmsdad -> ⦫ - new Transition (156, 157), // &angmsdae -> ⦬ - new Transition (158, 159), // &angmsdaf -> ⦭ - new Transition (160, 161), // &angmsdag -> ⦮ - new Transition (162, 163), // &angmsdah -> ⦯ - new Transition (165, 166), // &angrt -> ∟ - new Transition (168, 169), // &angrtvb -> ⊾ - new Transition (170, 171), // &angrtvbd -> ⦝ - new Transition (174, 175), // &angsph -> ∢ - new Transition (176, 177), // &angst -> Å - new Transition (181, 182), // &angzarr -> ⍼ - new Transition (186, 187), // &Aogon -> Ą - new Transition (191, 192), // &aogon -> ą - new Transition (194, 195), // &Aopf -> 𝔸 - new Transition (197, 198), // &aopf -> 𝕒 - new Transition (199, 200), // &ap -> ≈ - new Transition (204, 205), // &apacir -> ⩯ - new Transition (206, 207), // &apE -> ⩰ - new Transition (208, 209), // &ape -> ≊ - new Transition (211, 212), // &apid -> ≋ - new Transition (214, 215), // &apos -> ' - new Transition (227, 228), // &ApplyFunction -> ⁡ - new Transition (232, 233), // &approx -> ≈ - new Transition (235, 236), // &approxeq -> ≊ - new Transition (240, 241), // Å -> Å - new Transition (245, 246), // å -> å - new Transition (249, 250), // &Ascr -> 𝒜 - new Transition (253, 254), // &ascr -> 𝒶 - new Transition (258, 259), // &Assign -> ≔ - new Transition (260, 261), // &ast -> * - new Transition (264, 265), // &asymp -> ≈ - new Transition (267, 268), // &asympeq -> ≍ - new Transition (273, 274), // à -> à - new Transition (279, 280), // ã -> ã - new Transition (283, 284), // Ä -> Ä - new Transition (287, 288), // ä -> ä - new Transition (295, 296), // &awconint -> ∳ - new Transition (299, 300), // &awint -> ⨑ - new Transition (308, 309), // &backcong -> ≌ - new Transition (316, 317), // &backepsilon -> ϶ - new Transition (322, 323), // &backprime -> ‵ - new Transition (326, 327), // &backsim -> ∽ - new Transition (329, 330), // &backsimeq -> ⋍ - new Transition (339, 340), // &Backslash -> ∖ - new Transition (342, 343), // &Barv -> ⫧ - new Transition (347, 348), // &barvee -> ⊽ - new Transition (351, 352), // &Barwed -> ⌆ - new Transition (355, 356), // &barwed -> ⌅ - new Transition (358, 359), // &barwedge -> ⌅ - new Transition (362, 363), // &bbrk -> ⎵ - new Transition (367, 368), // &bbrktbrk -> ⎶ - new Transition (372, 373), // &bcong -> ≌ - new Transition (375, 376), // &Bcy -> Б - new Transition (377, 378), // &bcy -> б - new Transition (382, 383), // &bdquo -> „ - new Transition (388, 389), // &becaus -> ∵ - new Transition (395, 396), // &Because -> ∵ - new Transition (397, 398), // &because -> ∵ - new Transition (403, 404), // &bemptyv -> ⦰ - new Transition (407, 408), // &bepsi -> ϶ - new Transition (412, 413), // &bernou -> ℬ - new Transition (421, 422), // &Bernoullis -> ℬ - new Transition (424, 425), // &Beta -> Β - new Transition (427, 428), // &beta -> β - new Transition (429, 430), // &beth -> ℶ - new Transition (434, 435), // &between -> ≬ - new Transition (437, 438), // &Bfr -> 𝔅 - new Transition (440, 441), // &bfr -> 𝔟 - new Transition (446, 447), // &bigcap -> ⋂ - new Transition (450, 451), // &bigcirc -> ◯ - new Transition (453, 454), // &bigcup -> ⋃ - new Transition (458, 459), // &bigodot -> ⨀ - new Transition (463, 464), // &bigoplus -> ⨁ - new Transition (469, 470), // &bigotimes -> ⨂ - new Transition (475, 476), // &bigsqcup -> ⨆ - new Transition (479, 480), // &bigstar -> ★ - new Transition (492, 493), // &bigtriangledown -> ▽ - new Transition (495, 496), // &bigtriangleup -> △ - new Transition (501, 502), // &biguplus -> ⨄ - new Transition (505, 506), // &bigvee -> ⋁ - new Transition (511, 512), // &bigwedge -> ⋀ - new Transition (517, 518), // &bkarow -> ⤍ - new Transition (529, 530), // &blacklozenge -> ⧫ - new Transition (536, 537), // &blacksquare -> ▪ - new Transition (545, 546), // &blacktriangle -> ▴ - new Transition (550, 551), // &blacktriangledown -> ▾ - new Transition (555, 556), // &blacktriangleleft -> ◂ - new Transition (561, 562), // &blacktriangleright -> ▸ - new Transition (564, 565), // &blank -> ␣ - new Transition (568, 569), // &blk12 -> ▒ - new Transition (570, 571), // &blk14 -> ░ - new Transition (573, 574), // &blk34 -> ▓ - new Transition (577, 578), // &block -> █ - new Transition (580, 581), // &bne -> =⃥ - new Transition (585, 586), // &bnequiv -> ≡⃥ - new Transition (589, 590), // &bNot -> ⫭ - new Transition (592, 593), // &bnot -> ⌐ - new Transition (596, 597), // &Bopf -> 𝔹 - new Transition (600, 601), // &bopf -> 𝕓 - new Transition (602, 603), // &bot -> ⊥ - new Transition (606, 607), // &bottom -> ⊥ - new Transition (611, 612), // &bowtie -> ⋈ - new Transition (616, 617), // &boxbox -> ⧉ - new Transition (619, 620), // &boxDL -> ╗ - new Transition (621, 622), // &boxDl -> ╖ - new Transition (624, 625), // &boxdL -> ╕ - new Transition (626, 627), // &boxdl -> ┐ - new Transition (628, 629), // &boxDR -> ╔ - new Transition (630, 631), // &boxDr -> ╓ - new Transition (632, 633), // &boxdR -> ╒ - new Transition (634, 635), // &boxdr -> ┌ - new Transition (636, 637), // &boxH -> ═ - new Transition (638, 639), // &boxh -> ─ - new Transition (640, 641), // &boxHD -> ╦ - new Transition (642, 643), // &boxHd -> ╤ - new Transition (644, 645), // &boxhD -> ╥ - new Transition (646, 647), // &boxhd -> ┬ - new Transition (648, 649), // &boxHU -> ╩ - new Transition (650, 651), // &boxHu -> ╧ - new Transition (652, 653), // &boxhU -> ╨ - new Transition (654, 655), // &boxhu -> ┴ - new Transition (660, 661), // &boxminus -> ⊟ - new Transition (665, 666), // &boxplus -> ⊞ - new Transition (671, 672), // &boxtimes -> ⊠ - new Transition (674, 675), // &boxUL -> ╝ - new Transition (676, 677), // &boxUl -> ╜ - new Transition (679, 680), // &boxuL -> ╛ - new Transition (681, 682), // &boxul -> ┘ - new Transition (683, 684), // &boxUR -> ╚ - new Transition (685, 686), // &boxUr -> ╙ - new Transition (687, 688), // &boxuR -> ╘ - new Transition (689, 690), // &boxur -> └ - new Transition (691, 692), // &boxV -> ║ - new Transition (693, 694), // &boxv -> │ - new Transition (695, 696), // &boxVH -> ╬ - new Transition (697, 698), // &boxVh -> ╫ - new Transition (699, 700), // &boxvH -> ╪ - new Transition (701, 702), // &boxvh -> ┼ - new Transition (703, 704), // &boxVL -> ╣ - new Transition (705, 706), // &boxVl -> ╢ - new Transition (707, 708), // &boxvL -> ╡ - new Transition (709, 710), // &boxvl -> ┤ - new Transition (711, 712), // &boxVR -> ╠ - new Transition (713, 714), // &boxVr -> ╟ - new Transition (715, 716), // &boxvR -> ╞ - new Transition (717, 718), // &boxvr -> ├ - new Transition (723, 724), // &bprime -> ‵ - new Transition (728, 729), // &Breve -> ˘ - new Transition (733, 734), // &breve -> ˘ - new Transition (738, 739), // ¦ -> ¦ - new Transition (742, 743), // &Bscr -> ℬ - new Transition (746, 747), // &bscr -> 𝒷 - new Transition (750, 751), // &bsemi -> ⁏ - new Transition (753, 754), // &bsim -> ∽ - new Transition (755, 756), // &bsime -> ⋍ - new Transition (758, 759), // &bsol -> \ - new Transition (760, 761), // &bsolb -> ⧅ - new Transition (765, 766), // &bsolhsub -> ⟈ - new Transition (769, 770), // &bull -> • - new Transition (772, 773), // &bullet -> • - new Transition (775, 776), // &bump -> ≎ - new Transition (777, 778), // &bumpE -> ⪮ - new Transition (779, 780), // &bumpe -> ≏ - new Transition (785, 786), // &Bumpeq -> ≎ - new Transition (787, 788), // &bumpeq -> ≏ - new Transition (794, 795), // &Cacute -> Ć - new Transition (801, 802), // &cacute -> ć - new Transition (803, 804), // &Cap -> ⋒ - new Transition (805, 806), // &cap -> ∩ - new Transition (809, 810), // &capand -> ⩄ - new Transition (815, 816), // &capbrcup -> ⩉ - new Transition (819, 820), // &capcap -> ⩋ - new Transition (822, 823), // &capcup -> ⩇ - new Transition (826, 827), // &capdot -> ⩀ - new Transition (844, 845), // &CapitalDifferentialD -> ⅅ - new Transition (846, 847), // &caps -> ∩︀ - new Transition (850, 851), // &caret -> ⁁ - new Transition (853, 854), // &caron -> ˇ - new Transition (859, 860), // &Cayleys -> ℭ - new Transition (864, 865), // &ccaps -> ⩍ - new Transition (870, 871), // &Ccaron -> Č - new Transition (874, 875), // &ccaron -> č - new Transition (879, 880), // Ç -> Ç - new Transition (884, 885), // ç -> ç - new Transition (888, 889), // &Ccirc -> Ĉ - new Transition (892, 893), // &ccirc -> ĉ - new Transition (898, 899), // &Cconint -> ∰ - new Transition (902, 903), // &ccups -> ⩌ - new Transition (905, 906), // &ccupssm -> ⩐ - new Transition (909, 910), // &Cdot -> Ċ - new Transition (913, 914), // &cdot -> ċ - new Transition (918, 919), // ¸ -> ¸ - new Transition (925, 926), // &Cedilla -> ¸ - new Transition (931, 932), // &cemptyv -> ⦲ - new Transition (934, 935), // ¢ -> ¢ - new Transition (942, 943), // &CenterDot -> · - new Transition (948, 949), // ¢erdot -> · - new Transition (951, 952), // &Cfr -> ℭ - new Transition (954, 955), // &cfr -> 𝔠 - new Transition (958, 959), // &CHcy -> Ч - new Transition (962, 963), // &chcy -> ч - new Transition (966, 967), // &check -> ✓ - new Transition (971, 972), // &checkmark -> ✓ - new Transition (974, 975), // &Chi -> Χ - new Transition (976, 977), // &chi -> χ - new Transition (979, 980), // &cir -> ○ - new Transition (981, 982), // &circ -> ˆ - new Transition (984, 985), // &circeq -> ≗ - new Transition (996, 997), // &circlearrowleft -> ↺ - new Transition (1002, 1003), // &circlearrowright -> ↻ - new Transition (1007, 1008), // &circledast -> ⊛ - new Transition (1012, 1013), // &circledcirc -> ⊚ - new Transition (1017, 1018), // &circleddash -> ⊝ - new Transition (1026, 1027), // &CircleDot -> ⊙ - new Transition (1028, 1029), // &circledR -> ® - new Transition (1030, 1031), // &circledS -> Ⓢ - new Transition (1036, 1037), // &CircleMinus -> ⊖ - new Transition (1041, 1042), // &CirclePlus -> ⊕ - new Transition (1047, 1048), // &CircleTimes -> ⊗ - new Transition (1049, 1050), // &cirE -> ⧃ - new Transition (1051, 1052), // &cire -> ≗ - new Transition (1057, 1058), // &cirfnint -> ⨐ - new Transition (1061, 1062), // &cirmid -> ⫯ - new Transition (1066, 1067), // &cirscir -> ⧂ - new Transition (1090, 1091), // &ClockwiseContourIntegral -> ∲ - new Transition (1109, 1110), // &CloseCurlyDoubleQuote -> ” - new Transition (1115, 1116), // &CloseCurlyQuote -> ’ - new Transition (1120, 1121), // &clubs -> ♣ - new Transition (1124, 1125), // &clubsuit -> ♣ - new Transition (1129, 1130), // &Colon -> ∷ - new Transition (1134, 1135), // &colon -> : - new Transition (1136, 1137), // &Colone -> ⩴ - new Transition (1138, 1139), // &colone -> ≔ - new Transition (1140, 1141), // &coloneq -> ≔ - new Transition (1144, 1145), // &comma -> , - new Transition (1146, 1147), // &commat -> @ - new Transition (1148, 1149), // &comp -> ∁ - new Transition (1151, 1152), // &compfn -> ∘ - new Transition (1158, 1159), // &complement -> ∁ - new Transition (1162, 1163), // &complexes -> ℂ - new Transition (1165, 1166), // &cong -> ≅ - new Transition (1169, 1170), // &congdot -> ⩭ - new Transition (1177, 1178), // &Congruent -> ≡ - new Transition (1181, 1182), // &Conint -> ∯ - new Transition (1185, 1186), // &conint -> ∮ - new Transition (1198, 1199), // &ContourIntegral -> ∮ - new Transition (1201, 1202), // &Copf -> ℂ - new Transition (1204, 1205), // &copf -> 𝕔 - new Transition (1208, 1209), // &coprod -> ∐ - new Transition (1215, 1216), // &Coproduct -> ∐ - new Transition (1219, 1220), // © -> © - new Transition (1221, 1222), // © -> © - new Transition (1224, 1225), // ©sr -> ℗ - new Transition (1254, 1255), // &CounterClockwiseContourIntegral -> ∳ - new Transition (1259, 1260), // &crarr -> ↵ - new Transition (1264, 1265), // &Cross -> ⨯ - new Transition (1268, 1269), // &cross -> ✗ - new Transition (1272, 1273), // &Cscr -> 𝒞 - new Transition (1276, 1277), // &cscr -> 𝒸 - new Transition (1279, 1280), // &csub -> ⫏ - new Transition (1281, 1282), // &csube -> ⫑ - new Transition (1283, 1284), // &csup -> ⫐ - new Transition (1285, 1286), // &csupe -> ⫒ - new Transition (1290, 1291), // &ctdot -> ⋯ - new Transition (1297, 1298), // &cudarrl -> ⤸ - new Transition (1299, 1300), // &cudarrr -> ⤵ - new Transition (1303, 1304), // &cuepr -> ⋞ - new Transition (1306, 1307), // &cuesc -> ⋟ - new Transition (1311, 1312), // &cularr -> ↶ - new Transition (1313, 1314), // &cularrp -> ⤽ - new Transition (1316, 1317), // &Cup -> ⋓ - new Transition (1318, 1319), // &cup -> ∪ - new Transition (1324, 1325), // &cupbrcap -> ⩈ - new Transition (1328, 1329), // &CupCap -> ≍ - new Transition (1332, 1333), // &cupcap -> ⩆ - new Transition (1335, 1336), // &cupcup -> ⩊ - new Transition (1339, 1340), // &cupdot -> ⊍ - new Transition (1342, 1343), // &cupor -> ⩅ - new Transition (1344, 1345), // &cups -> ∪︀ - new Transition (1349, 1350), // &curarr -> ↷ - new Transition (1351, 1352), // &curarrm -> ⤼ - new Transition (1360, 1361), // &curlyeqprec -> ⋞ - new Transition (1365, 1366), // &curlyeqsucc -> ⋟ - new Transition (1369, 1370), // &curlyvee -> ⋎ - new Transition (1375, 1376), // &curlywedge -> ⋏ - new Transition (1379, 1380), // ¤ -> ¤ - new Transition (1391, 1392), // &curvearrowleft -> ↶ - new Transition (1397, 1398), // &curvearrowright -> ↷ - new Transition (1401, 1402), // &cuvee -> ⋎ - new Transition (1405, 1406), // &cuwed -> ⋏ - new Transition (1413, 1414), // &cwconint -> ∲ - new Transition (1417, 1418), // &cwint -> ∱ - new Transition (1423, 1424), // &cylcty -> ⌭ - new Transition (1430, 1431), // &Dagger -> ‡ - new Transition (1437, 1438), // &dagger -> † - new Transition (1442, 1443), // &daleth -> ℸ - new Transition (1445, 1446), // &Darr -> ↡ - new Transition (1449, 1450), // &dArr -> ⇓ - new Transition (1452, 1453), // &darr -> ↓ - new Transition (1455, 1456), // &dash -> ‐ - new Transition (1459, 1460), // &Dashv -> ⫤ - new Transition (1461, 1462), // &dashv -> ⊣ - new Transition (1468, 1469), // &dbkarow -> ⤏ - new Transition (1472, 1473), // &dblac -> ˝ - new Transition (1478, 1479), // &Dcaron -> Ď - new Transition (1484, 1485), // &dcaron -> ď - new Transition (1486, 1487), // &Dcy -> Д - new Transition (1488, 1489), // &dcy -> д - new Transition (1490, 1491), // &DD -> ⅅ - new Transition (1492, 1493), // &dd -> ⅆ - new Transition (1498, 1499), // &ddagger -> ‡ - new Transition (1501, 1502), // &ddarr -> ⇊ - new Transition (1508, 1509), // &DDotrahd -> ⤑ - new Transition (1514, 1515), // &ddotseq -> ⩷ - new Transition (1517, 1518), // ° -> ° - new Transition (1520, 1521), // &Del -> ∇ - new Transition (1523, 1524), // &Delta -> Δ - new Transition (1527, 1528), // &delta -> δ - new Transition (1533, 1534), // &demptyv -> ⦱ - new Transition (1539, 1540), // &dfisht -> ⥿ - new Transition (1542, 1543), // &Dfr -> 𝔇 - new Transition (1544, 1545), // &dfr -> 𝔡 - new Transition (1548, 1549), // &dHar -> ⥥ - new Transition (1553, 1554), // &dharl -> ⇃ - new Transition (1555, 1556), // &dharr -> ⇂ - new Transition (1571, 1572), // &DiacriticalAcute -> ´ - new Transition (1575, 1576), // &DiacriticalDot -> ˙ - new Transition (1585, 1586), // &DiacriticalDoubleAcute -> ˝ - new Transition (1591, 1592), // &DiacriticalGrave -> ` - new Transition (1597, 1598), // &DiacriticalTilde -> ˜ - new Transition (1601, 1602), // &diam -> ⋄ - new Transition (1606, 1607), // &Diamond -> ⋄ - new Transition (1610, 1611), // &diamond -> ⋄ - new Transition (1615, 1616), // &diamondsuit -> ♦ - new Transition (1617, 1618), // &diams -> ♦ - new Transition (1619, 1620), // &die -> ¨ - new Transition (1631, 1632), // &DifferentialD -> ⅆ - new Transition (1637, 1638), // &digamma -> ϝ - new Transition (1641, 1642), // &disin -> ⋲ - new Transition (1643, 1644), // &div -> ÷ - new Transition (1647, 1648), // ÷ -> ÷ - new Transition (1655, 1656), // ÷ontimes -> ⋇ - new Transition (1659, 1660), // &divonx -> ⋇ - new Transition (1663, 1664), // &DJcy -> Ђ - new Transition (1667, 1668), // &djcy -> ђ - new Transition (1673, 1674), // &dlcorn -> ⌞ - new Transition (1677, 1678), // &dlcrop -> ⌍ - new Transition (1683, 1684), // &dollar -> $ - new Transition (1687, 1688), // &Dopf -> 𝔻 - new Transition (1690, 1691), // &dopf -> 𝕕 - new Transition (1692, 1693), // &Dot -> ¨ - new Transition (1694, 1695), // &dot -> ˙ - new Transition (1698, 1699), // &DotDot -> ⃜ - new Transition (1701, 1702), // &doteq -> ≐ - new Transition (1705, 1706), // &doteqdot -> ≑ - new Transition (1711, 1712), // &DotEqual -> ≐ - new Transition (1717, 1718), // &dotminus -> ∸ - new Transition (1722, 1723), // &dotplus -> ∔ - new Transition (1729, 1730), // &dotsquare -> ⊡ - new Transition (1742, 1743), // &doublebarwedge -> ⌆ - new Transition (1762, 1763), // &DoubleContourIntegral -> ∯ - new Transition (1766, 1767), // &DoubleDot -> ¨ - new Transition (1774, 1775), // &DoubleDownArrow -> ⇓ - new Transition (1784, 1785), // &DoubleLeftArrow -> ⇐ - new Transition (1795, 1796), // &DoubleLeftRightArrow -> ⇔ - new Transition (1799, 1800), // &DoubleLeftTee -> ⫤ - new Transition (1812, 1813), // &DoubleLongLeftArrow -> ⟸ - new Transition (1823, 1824), // &DoubleLongLeftRightArrow -> ⟺ - new Transition (1834, 1835), // &DoubleLongRightArrow -> ⟹ - new Transition (1845, 1846), // &DoubleRightArrow -> ⇒ - new Transition (1849, 1850), // &DoubleRightTee -> ⊨ - new Transition (1857, 1858), // &DoubleUpArrow -> ⇑ - new Transition (1867, 1868), // &DoubleUpDownArrow -> ⇕ - new Transition (1879, 1880), // &DoubleVerticalBar -> ∥ - new Transition (1887, 1888), // &DownArrow -> ↓ - new Transition (1893, 1894), // &Downarrow -> ⇓ - new Transition (1901, 1902), // &downarrow -> ↓ - new Transition (1905, 1906), // &DownArrowBar -> ⤓ - new Transition (1913, 1914), // &DownArrowUpArrow -> ⇵ - new Transition (1919, 1920), // &DownBreve -> ̑ - new Transition (1930, 1931), // &downdownarrows -> ⇊ - new Transition (1942, 1943), // &downharpoonleft -> ⇃ - new Transition (1948, 1949), // &downharpoonright -> ⇂ - new Transition (1964, 1965), // &DownLeftRightVector -> ⥐ - new Transition (1974, 1975), // &DownLeftTeeVector -> ⥞ - new Transition (1981, 1982), // &DownLeftVector -> ↽ - new Transition (1985, 1986), // &DownLeftVectorBar -> ⥖ - new Transition (2000, 2001), // &DownRightTeeVector -> ⥟ - new Transition (2007, 2008), // &DownRightVector -> ⇁ - new Transition (2011, 2012), // &DownRightVectorBar -> ⥗ - new Transition (2015, 2016), // &DownTee -> ⊤ - new Transition (2021, 2022), // &DownTeeArrow -> ↧ - new Transition (2029, 2030), // &drbkarow -> ⤐ - new Transition (2034, 2035), // &drcorn -> ⌟ - new Transition (2038, 2039), // &drcrop -> ⌌ - new Transition (2042, 2043), // &Dscr -> 𝒟 - new Transition (2046, 2047), // &dscr -> 𝒹 - new Transition (2050, 2051), // &DScy -> Ѕ - new Transition (2052, 2053), // &dscy -> ѕ - new Transition (2055, 2056), // &dsol -> ⧶ - new Transition (2060, 2061), // &Dstrok -> Đ - new Transition (2065, 2066), // &dstrok -> đ - new Transition (2070, 2071), // &dtdot -> ⋱ - new Transition (2073, 2074), // &dtri -> ▿ - new Transition (2075, 2076), // &dtrif -> ▾ - new Transition (2080, 2081), // &duarr -> ⇵ - new Transition (2084, 2085), // &duhar -> ⥯ - new Transition (2091, 2092), // &dwangle -> ⦦ - new Transition (2095, 2096), // &DZcy -> Џ - new Transition (2099, 2100), // &dzcy -> џ - new Transition (2106, 2107), // &dzigrarr -> ⟿ - new Transition (2113, 2114), // É -> É - new Transition (2120, 2121), // é -> é - new Transition (2125, 2126), // &easter -> ⩮ - new Transition (2131, 2132), // &Ecaron -> Ě - new Transition (2137, 2138), // &ecaron -> ě - new Transition (2140, 2141), // &ecir -> ≖ - new Transition (2144, 2145), // Ê -> Ê - new Transition (2146, 2147), // ê -> ê - new Transition (2151, 2152), // &ecolon -> ≕ - new Transition (2153, 2154), // &Ecy -> Э - new Transition (2155, 2156), // &ecy -> э - new Transition (2160, 2161), // &eDDot -> ⩷ - new Transition (2164, 2165), // &Edot -> Ė - new Transition (2167, 2168), // &eDot -> ≑ - new Transition (2171, 2172), // &edot -> ė - new Transition (2173, 2174), // &ee -> ⅇ - new Transition (2178, 2179), // &efDot -> ≒ - new Transition (2181, 2182), // &Efr -> 𝔈 - new Transition (2183, 2184), // &efr -> 𝔢 - new Transition (2185, 2186), // &eg -> ⪚ - new Transition (2191, 2192), // È -> È - new Transition (2196, 2197), // è -> è - new Transition (2198, 2199), // &egs -> ⪖ - new Transition (2202, 2203), // &egsdot -> ⪘ - new Transition (2204, 2205), // &el -> ⪙ - new Transition (2211, 2212), // &Element -> ∈ - new Transition (2218, 2219), // &elinters -> ⏧ - new Transition (2220, 2221), // &ell -> ℓ - new Transition (2222, 2223), // &els -> ⪕ - new Transition (2226, 2227), // &elsdot -> ⪗ - new Transition (2231, 2232), // &Emacr -> Ē - new Transition (2236, 2237), // &emacr -> ē - new Transition (2240, 2241), // &empty -> ∅ - new Transition (2244, 2245), // &emptyset -> ∅ - new Transition (2259, 2260), // &EmptySmallSquare -> ◻ - new Transition (2261, 2262), // &emptyv -> ∅ - new Transition (2277, 2278), // &EmptyVerySmallSquare -> ▫ - new Transition (2280, 2281), // &emsp ->   - new Transition (2283, 2284), // &emsp13 ->   - new Transition (2285, 2286), // &emsp14 ->   - new Transition (2288, 2289), // &ENG -> Ŋ - new Transition (2291, 2292), // &eng -> ŋ - new Transition (2294, 2295), // &ensp ->   - new Transition (2299, 2300), // &Eogon -> Ę - new Transition (2304, 2305), // &eogon -> ę - new Transition (2307, 2308), // &Eopf -> 𝔼 - new Transition (2310, 2311), // &eopf -> 𝕖 - new Transition (2314, 2315), // &epar -> ⋕ - new Transition (2317, 2318), // &eparsl -> ⧣ - new Transition (2321, 2322), // &eplus -> ⩱ - new Transition (2324, 2325), // &epsi -> ε - new Transition (2331, 2332), // &Epsilon -> Ε - new Transition (2335, 2336), // &epsilon -> ε - new Transition (2337, 2338), // &epsiv -> ϵ - new Transition (2343, 2344), // &eqcirc -> ≖ - new Transition (2348, 2349), // &eqcolon -> ≕ - new Transition (2352, 2353), // &eqsim -> ≂ - new Transition (2360, 2361), // &eqslantgtr -> ⪖ - new Transition (2365, 2366), // &eqslantless -> ⪕ - new Transition (2370, 2371), // &Equal -> ⩵ - new Transition (2375, 2376), // &equals -> = - new Transition (2381, 2382), // &EqualTilde -> ≂ - new Transition (2385, 2386), // &equest -> ≟ - new Transition (2394, 2395), // &Equilibrium -> ⇌ - new Transition (2397, 2398), // &equiv -> ≡ - new Transition (2400, 2401), // &equivDD -> ⩸ - new Transition (2407, 2408), // &eqvparsl -> ⧥ - new Transition (2412, 2413), // &erarr -> ⥱ - new Transition (2416, 2417), // &erDot -> ≓ - new Transition (2420, 2421), // &Escr -> ℰ - new Transition (2424, 2425), // &escr -> ℯ - new Transition (2428, 2429), // &esdot -> ≐ - new Transition (2431, 2432), // &Esim -> ⩳ - new Transition (2434, 2435), // &esim -> ≂ - new Transition (2437, 2438), // &Eta -> Η - new Transition (2440, 2441), // &eta -> η - new Transition (2443, 2444), // Ð -> Ð - new Transition (2445, 2446), // ð -> ð - new Transition (2449, 2450), // Ë -> Ë - new Transition (2453, 2454), // ë -> ë - new Transition (2456, 2457), // &euro -> € - new Transition (2460, 2461), // &excl -> ! - new Transition (2464, 2465), // &exist -> ∃ - new Transition (2470, 2471), // &Exists -> ∃ - new Transition (2480, 2481), // &expectation -> ℰ - new Transition (2491, 2492), // &ExponentialE -> ⅇ - new Transition (2501, 2502), // &exponentiale -> ⅇ - new Transition (2515, 2516), // &fallingdotseq -> ≒ - new Transition (2519, 2520), // &Fcy -> Ф - new Transition (2522, 2523), // &fcy -> ф - new Transition (2528, 2529), // &female -> ♀ - new Transition (2534, 2535), // &ffilig -> ffi - new Transition (2538, 2539), // &fflig -> ff - new Transition (2542, 2543), // &ffllig -> ffl - new Transition (2545, 2546), // &Ffr -> 𝔉 - new Transition (2547, 2548), // &ffr -> 𝔣 - new Transition (2552, 2553), // &filig -> fi - new Transition (2569, 2570), // &FilledSmallSquare -> ◼ - new Transition (2585, 2586), // &FilledVerySmallSquare -> ▪ - new Transition (2590, 2591), // &fjlig -> fj - new Transition (2594, 2595), // &flat -> ♭ - new Transition (2598, 2599), // &fllig -> fl - new Transition (2602, 2603), // &fltns -> ▱ - new Transition (2606, 2607), // &fnof -> ƒ - new Transition (2610, 2611), // &Fopf -> 𝔽 - new Transition (2614, 2615), // &fopf -> 𝕗 - new Transition (2619, 2620), // &ForAll -> ∀ - new Transition (2624, 2625), // &forall -> ∀ - new Transition (2626, 2627), // &fork -> ⋔ - new Transition (2628, 2629), // &forkv -> ⫙ - new Transition (2637, 2638), // &Fouriertrf -> ℱ - new Transition (2645, 2646), // &fpartint -> ⨍ - new Transition (2651, 2652), // ½ -> ½ - new Transition (2653, 2654), // &frac13 -> ⅓ - new Transition (2655, 2656), // ¼ -> ¼ - new Transition (2657, 2658), // &frac15 -> ⅕ - new Transition (2659, 2660), // &frac16 -> ⅙ - new Transition (2661, 2662), // &frac18 -> ⅛ - new Transition (2664, 2665), // &frac23 -> ⅔ - new Transition (2666, 2667), // &frac25 -> ⅖ - new Transition (2669, 2670), // ¾ -> ¾ - new Transition (2671, 2672), // &frac35 -> ⅗ - new Transition (2673, 2674), // &frac38 -> ⅜ - new Transition (2676, 2677), // &frac45 -> ⅘ - new Transition (2679, 2680), // &frac56 -> ⅚ - new Transition (2681, 2682), // &frac58 -> ⅝ - new Transition (2684, 2685), // &frac78 -> ⅞ - new Transition (2687, 2688), // &frasl -> ⁄ - new Transition (2691, 2692), // &frown -> ⌢ - new Transition (2695, 2696), // &Fscr -> ℱ - new Transition (2699, 2700), // &fscr -> 𝒻 - new Transition (2706, 2707), // &gacute -> ǵ - new Transition (2712, 2713), // &Gamma -> Γ - new Transition (2716, 2717), // &gamma -> γ - new Transition (2718, 2719), // &Gammad -> Ϝ - new Transition (2720, 2721), // &gammad -> ϝ - new Transition (2722, 2723), // &gap -> ⪆ - new Transition (2728, 2729), // &Gbreve -> Ğ - new Transition (2734, 2735), // &gbreve -> ğ - new Transition (2740, 2741), // &Gcedil -> Ģ - new Transition (2744, 2745), // &Gcirc -> Ĝ - new Transition (2749, 2750), // &gcirc -> ĝ - new Transition (2751, 2752), // &Gcy -> Г - new Transition (2753, 2754), // &gcy -> г - new Transition (2757, 2758), // &Gdot -> Ġ - new Transition (2761, 2762), // &gdot -> ġ - new Transition (2763, 2764), // &gE -> ≧ - new Transition (2765, 2766), // &ge -> ≥ - new Transition (2767, 2768), // &gEl -> ⪌ - new Transition (2769, 2770), // &gel -> ⋛ - new Transition (2771, 2772), // &geq -> ≥ - new Transition (2773, 2774), // &geqq -> ≧ - new Transition (2779, 2780), // &geqslant -> ⩾ - new Transition (2781, 2782), // &ges -> ⩾ - new Transition (2784, 2785), // &gescc -> ⪩ - new Transition (2788, 2789), // &gesdot -> ⪀ - new Transition (2790, 2791), // &gesdoto -> ⪂ - new Transition (2792, 2793), // &gesdotol -> ⪄ - new Transition (2794, 2795), // &gesl -> ⋛︀ - new Transition (2797, 2798), // &gesles -> ⪔ - new Transition (2800, 2801), // &Gfr -> 𝔊 - new Transition (2803, 2804), // &gfr -> 𝔤 - new Transition (2805, 2806), // &Gg -> ⋙ - new Transition (2807, 2808), // &gg -> ≫ - new Transition (2809, 2810), // &ggg -> ⋙ - new Transition (2814, 2815), // &gimel -> ℷ - new Transition (2818, 2819), // &GJcy -> Ѓ - new Transition (2822, 2823), // &gjcy -> ѓ - new Transition (2824, 2825), // &gl -> ≷ - new Transition (2826, 2827), // &gla -> ⪥ - new Transition (2828, 2829), // &glE -> ⪒ - new Transition (2830, 2831), // &glj -> ⪤ - new Transition (2834, 2835), // &gnap -> ⪊ - new Transition (2839, 2840), // &gnapprox -> ⪊ - new Transition (2841, 2842), // &gnE -> ≩ - new Transition (2843, 2844), // &gne -> ⪈ - new Transition (2845, 2846), // &gneq -> ⪈ - new Transition (2847, 2848), // &gneqq -> ≩ - new Transition (2851, 2852), // &gnsim -> ⋧ - new Transition (2855, 2856), // &Gopf -> 𝔾 - new Transition (2859, 2860), // &gopf -> 𝕘 - new Transition (2864, 2865), // &grave -> ` - new Transition (2876, 2877), // &GreaterEqual -> ≥ - new Transition (2881, 2882), // &GreaterEqualLess -> ⋛ - new Transition (2891, 2892), // &GreaterFullEqual -> ≧ - new Transition (2899, 2900), // &GreaterGreater -> ⪢ - new Transition (2904, 2905), // &GreaterLess -> ≷ - new Transition (2915, 2916), // &GreaterSlantEqual -> ⩾ - new Transition (2921, 2922), // &GreaterTilde -> ≳ - new Transition (2925, 2926), // &Gscr -> 𝒢 - new Transition (2929, 2930), // &gscr -> ℊ - new Transition (2932, 2933), // &gsim -> ≳ - new Transition (2934, 2935), // &gsime -> ⪎ - new Transition (2936, 2937), // &gsiml -> ⪐ - new Transition (2938, 2939), // > -> > - new Transition (2940, 2941), // &Gt -> ≫ - new Transition (2942, 2943), // > -> > - new Transition (2945, 2946), // >cc -> ⪧ - new Transition (2948, 2949), // >cir -> ⩺ - new Transition (2952, 2953), // >dot -> ⋗ - new Transition (2957, 2958), // >lPar -> ⦕ - new Transition (2963, 2964), // >quest -> ⩼ - new Transition (2971, 2972), // >rapprox -> ⪆ - new Transition (2974, 2975), // >rarr -> ⥸ - new Transition (2978, 2979), // >rdot -> ⋗ - new Transition (2985, 2986), // >reqless -> ⋛ - new Transition (2991, 2992), // >reqqless -> ⪌ - new Transition (2996, 2997), // >rless -> ≷ - new Transition (3000, 3001), // >rsim -> ≳ - new Transition (3009, 3010), // &gvertneqq -> ≩︀ - new Transition (3012, 3013), // &gvnE -> ≩︀ - new Transition (3018, 3019), // &Hacek -> ˇ - new Transition (3025, 3026), // &hairsp ->   - new Transition (3028, 3029), // &half -> ½ - new Transition (3033, 3034), // &hamilt -> ℋ - new Transition (3039, 3040), // &HARDcy -> Ъ - new Transition (3044, 3045), // &hardcy -> ъ - new Transition (3048, 3049), // &hArr -> ⇔ - new Transition (3050, 3051), // &harr -> ↔ - new Transition (3054, 3055), // &harrcir -> ⥈ - new Transition (3056, 3057), // &harrw -> ↭ - new Transition (3058, 3059), // &Hat -> ^ - new Transition (3062, 3063), // &hbar -> ℏ - new Transition (3067, 3068), // &Hcirc -> Ĥ - new Transition (3072, 3073), // &hcirc -> ĥ - new Transition (3078, 3079), // &hearts -> ♥ - new Transition (3082, 3083), // &heartsuit -> ♥ - new Transition (3087, 3088), // &hellip -> … - new Transition (3092, 3093), // &hercon -> ⊹ - new Transition (3095, 3096), // &Hfr -> ℌ - new Transition (3098, 3099), // &hfr -> 𝔥 - new Transition (3110, 3111), // &HilbertSpace -> ℋ - new Transition (3118, 3119), // &hksearow -> ⤥ - new Transition (3124, 3125), // &hkswarow -> ⤦ - new Transition (3129, 3130), // &hoarr -> ⇿ - new Transition (3134, 3135), // &homtht -> ∻ - new Transition (3146, 3147), // &hookleftarrow -> ↩ - new Transition (3157, 3158), // &hookrightarrow -> ↪ - new Transition (3161, 3162), // &Hopf -> ℍ - new Transition (3164, 3165), // &hopf -> 𝕙 - new Transition (3169, 3170), // &horbar -> ― - new Transition (3182, 3183), // &HorizontalLine -> ─ - new Transition (3186, 3187), // &Hscr -> ℋ - new Transition (3190, 3191), // &hscr -> 𝒽 - new Transition (3195, 3196), // &hslash -> ℏ - new Transition (3200, 3201), // &Hstrok -> Ħ - new Transition (3205, 3206), // &hstrok -> ħ - new Transition (3217, 3218), // &HumpDownHump -> ≎ - new Transition (3223, 3224), // &HumpEqual -> ≏ - new Transition (3229, 3230), // &hybull -> ⁃ - new Transition (3234, 3235), // &hyphen -> ‐ - new Transition (3241, 3242), // Í -> Í - new Transition (3248, 3249), // í -> í - new Transition (3250, 3251), // &ic -> ⁣ - new Transition (3255, 3256), // Î -> Î - new Transition (3259, 3260), // î -> î - new Transition (3261, 3262), // &Icy -> И - new Transition (3263, 3264), // &icy -> и - new Transition (3267, 3268), // &Idot -> İ - new Transition (3271, 3272), // &IEcy -> Е - new Transition (3275, 3276), // &iecy -> е - new Transition (3279, 3280), // ¡ -> ¡ - new Transition (3282, 3283), // &iff -> ⇔ - new Transition (3285, 3286), // &Ifr -> ℑ - new Transition (3287, 3288), // &ifr -> 𝔦 - new Transition (3293, 3294), // Ì -> Ì - new Transition (3299, 3300), // ì -> ì - new Transition (3301, 3302), // &ii -> ⅈ - new Transition (3306, 3307), // &iiiint -> ⨌ - new Transition (3309, 3310), // &iiint -> ∭ - new Transition (3314, 3315), // &iinfin -> ⧜ - new Transition (3318, 3319), // &iiota -> ℩ - new Transition (3323, 3324), // &IJlig -> IJ - new Transition (3328, 3329), // &ijlig -> ij - new Transition (3330, 3331), // &Im -> ℑ - new Transition (3334, 3335), // &Imacr -> Ī - new Transition (3339, 3340), // &imacr -> ī - new Transition (3342, 3343), // &image -> ℑ - new Transition (3350, 3351), // &ImaginaryI -> ⅈ - new Transition (3355, 3356), // &imagline -> ℐ - new Transition (3360, 3361), // &imagpart -> ℑ - new Transition (3363, 3364), // &imath -> ı - new Transition (3366, 3367), // &imof -> ⊷ - new Transition (3370, 3371), // &imped -> Ƶ - new Transition (3376, 3377), // &Implies -> ⇒ - new Transition (3378, 3379), // &in -> ∈ - new Transition (3383, 3384), // &incare -> ℅ - new Transition (3387, 3388), // &infin -> ∞ - new Transition (3391, 3392), // &infintie -> ⧝ - new Transition (3396, 3397), // &inodot -> ı - new Transition (3399, 3400), // &Int -> ∬ - new Transition (3401, 3402), // &int -> ∫ - new Transition (3405, 3406), // &intcal -> ⊺ - new Transition (3411, 3412), // &integers -> ℤ - new Transition (3417, 3418), // &Integral -> ∫ - new Transition (3422, 3423), // &intercal -> ⊺ - new Transition (3431, 3432), // &Intersection -> ⋂ - new Transition (3437, 3438), // &intlarhk -> ⨗ - new Transition (3442, 3443), // &intprod -> ⨼ - new Transition (3455, 3456), // &InvisibleComma -> ⁣ - new Transition (3461, 3462), // &InvisibleTimes -> ⁢ - new Transition (3465, 3466), // &IOcy -> Ё - new Transition (3469, 3470), // &iocy -> ё - new Transition (3474, 3475), // &Iogon -> Į - new Transition (3478, 3479), // &iogon -> į - new Transition (3481, 3482), // &Iopf -> 𝕀 - new Transition (3484, 3485), // &iopf -> 𝕚 - new Transition (3487, 3488), // &Iota -> Ι - new Transition (3490, 3491), // &iota -> ι - new Transition (3495, 3496), // &iprod -> ⨼ - new Transition (3501, 3502), // ¿ -> ¿ - new Transition (3505, 3506), // &Iscr -> ℐ - new Transition (3509, 3510), // &iscr -> 𝒾 - new Transition (3512, 3513), // &isin -> ∈ - new Transition (3516, 3517), // &isindot -> ⋵ - new Transition (3518, 3519), // &isinE -> ⋹ - new Transition (3520, 3521), // &isins -> ⋴ - new Transition (3522, 3523), // &isinsv -> ⋳ - new Transition (3524, 3525), // &isinv -> ∈ - new Transition (3526, 3527), // &it -> ⁢ - new Transition (3532, 3533), // &Itilde -> Ĩ - new Transition (3537, 3538), // &itilde -> ĩ - new Transition (3542, 3543), // &Iukcy -> І - new Transition (3547, 3548), // &iukcy -> і - new Transition (3550, 3551), // Ï -> Ï - new Transition (3553, 3554), // ï -> ï - new Transition (3559, 3560), // &Jcirc -> Ĵ - new Transition (3565, 3566), // &jcirc -> ĵ - new Transition (3567, 3568), // &Jcy -> Й - new Transition (3569, 3570), // &jcy -> й - new Transition (3572, 3573), // &Jfr -> 𝔍 - new Transition (3575, 3576), // &jfr -> 𝔧 - new Transition (3580, 3581), // &jmath -> ȷ - new Transition (3584, 3585), // &Jopf -> 𝕁 - new Transition (3588, 3589), // &jopf -> 𝕛 - new Transition (3592, 3593), // &Jscr -> 𝒥 - new Transition (3596, 3597), // &jscr -> 𝒿 - new Transition (3601, 3602), // &Jsercy -> Ј - new Transition (3606, 3607), // &jsercy -> ј - new Transition (3611, 3612), // &Jukcy -> Є - new Transition (3616, 3617), // &jukcy -> є - new Transition (3622, 3623), // &Kappa -> Κ - new Transition (3628, 3629), // &kappa -> κ - new Transition (3630, 3631), // &kappav -> ϰ - new Transition (3636, 3637), // &Kcedil -> Ķ - new Transition (3642, 3643), // &kcedil -> ķ - new Transition (3644, 3645), // &Kcy -> К - new Transition (3646, 3647), // &kcy -> к - new Transition (3649, 3650), // &Kfr -> 𝔎 - new Transition (3652, 3653), // &kfr -> 𝔨 - new Transition (3658, 3659), // &kgreen -> ĸ - new Transition (3662, 3663), // &KHcy -> Х - new Transition (3666, 3667), // &khcy -> х - new Transition (3670, 3671), // &KJcy -> Ќ - new Transition (3674, 3675), // &kjcy -> ќ - new Transition (3678, 3679), // &Kopf -> 𝕂 - new Transition (3682, 3683), // &kopf -> 𝕜 - new Transition (3686, 3687), // &Kscr -> 𝒦 - new Transition (3690, 3691), // &kscr -> 𝓀 - new Transition (3696, 3697), // &lAarr -> ⇚ - new Transition (3703, 3704), // &Lacute -> Ĺ - new Transition (3709, 3710), // &lacute -> ĺ - new Transition (3716, 3717), // &laemptyv -> ⦴ - new Transition (3721, 3722), // &lagran -> ℒ - new Transition (3726, 3727), // &Lambda -> Λ - new Transition (3731, 3732), // &lambda -> λ - new Transition (3734, 3735), // &Lang -> ⟪ - new Transition (3737, 3738), // &lang -> ⟨ - new Transition (3739, 3740), // &langd -> ⦑ - new Transition (3742, 3743), // &langle -> ⟨ - new Transition (3744, 3745), // &lap -> ⪅ - new Transition (3753, 3754), // &Laplacetrf -> ℒ - new Transition (3757, 3758), // « -> « - new Transition (3760, 3761), // &Larr -> ↞ - new Transition (3763, 3764), // &lArr -> ⇐ - new Transition (3766, 3767), // &larr -> ← - new Transition (3768, 3769), // &larrb -> ⇤ - new Transition (3771, 3772), // &larrbfs -> ⤟ - new Transition (3774, 3775), // &larrfs -> ⤝ - new Transition (3777, 3778), // &larrhk -> ↩ - new Transition (3780, 3781), // &larrlp -> ↫ - new Transition (3783, 3784), // &larrpl -> ⤹ - new Transition (3787, 3788), // &larrsim -> ⥳ - new Transition (3790, 3791), // &larrtl -> ↢ - new Transition (3792, 3793), // &lat -> ⪫ - new Transition (3797, 3798), // &lAtail -> ⤛ - new Transition (3801, 3802), // &latail -> ⤙ - new Transition (3803, 3804), // &late -> ⪭ - new Transition (3805, 3806), // &lates -> ⪭︀ - new Transition (3810, 3811), // &lBarr -> ⤎ - new Transition (3815, 3816), // &lbarr -> ⤌ - new Transition (3819, 3820), // &lbbrk -> ❲ - new Transition (3824, 3825), // &lbrace -> { - new Transition (3826, 3827), // &lbrack -> [ - new Transition (3829, 3830), // &lbrke -> ⦋ - new Transition (3833, 3834), // &lbrksld -> ⦏ - new Transition (3835, 3836), // &lbrkslu -> ⦍ - new Transition (3841, 3842), // &Lcaron -> Ľ - new Transition (3847, 3848), // &lcaron -> ľ - new Transition (3852, 3853), // &Lcedil -> Ļ - new Transition (3857, 3858), // &lcedil -> ļ - new Transition (3860, 3861), // &lceil -> ⌈ - new Transition (3863, 3864), // &lcub -> { - new Transition (3865, 3866), // &Lcy -> Л - new Transition (3867, 3868), // &lcy -> л - new Transition (3871, 3872), // &ldca -> ⤶ - new Transition (3875, 3876), // &ldquo -> “ - new Transition (3877, 3878), // &ldquor -> „ - new Transition (3883, 3884), // &ldrdhar -> ⥧ - new Transition (3889, 3890), // &ldrushar -> ⥋ - new Transition (3892, 3893), // &ldsh -> ↲ - new Transition (3894, 3895), // &lE -> ≦ - new Transition (3896, 3897), // &le -> ≤ - new Transition (3912, 3913), // &LeftAngleBracket -> ⟨ - new Transition (3917, 3918), // &LeftArrow -> ← - new Transition (3923, 3924), // &Leftarrow -> ⇐ - new Transition (3931, 3932), // &leftarrow -> ← - new Transition (3935, 3936), // &LeftArrowBar -> ⇤ - new Transition (3946, 3947), // &LeftArrowRightArrow -> ⇆ - new Transition (3951, 3952), // &leftarrowtail -> ↢ - new Transition (3959, 3960), // &LeftCeiling -> ⌈ - new Transition (3973, 3974), // &LeftDoubleBracket -> ⟦ - new Transition (3985, 3986), // &LeftDownTeeVector -> ⥡ - new Transition (3992, 3993), // &LeftDownVector -> ⇃ - new Transition (3996, 3997), // &LeftDownVectorBar -> ⥙ - new Transition (4002, 4003), // &LeftFloor -> ⌊ - new Transition (4014, 4015), // &leftharpoondown -> ↽ - new Transition (4017, 4018), // &leftharpoonup -> ↼ - new Transition (4028, 4029), // &leftleftarrows -> ⇇ - new Transition (4039, 4040), // &LeftRightArrow -> ↔ - new Transition (4050, 4051), // &Leftrightarrow -> ⇔ - new Transition (4061, 4062), // &leftrightarrow -> ↔ - new Transition (4063, 4064), // &leftrightarrows -> ⇆ - new Transition (4072, 4073), // &leftrightharpoons -> ⇋ - new Transition (4083, 4084), // &leftrightsquigarrow -> ↭ - new Transition (4090, 4091), // &LeftRightVector -> ⥎ - new Transition (4094, 4095), // &LeftTee -> ⊣ - new Transition (4100, 4101), // &LeftTeeArrow -> ↤ - new Transition (4107, 4108), // &LeftTeeVector -> ⥚ - new Transition (4118, 4119), // &leftthreetimes -> ⋋ - new Transition (4126, 4127), // &LeftTriangle -> ⊲ - new Transition (4130, 4131), // &LeftTriangleBar -> ⧏ - new Transition (4136, 4137), // &LeftTriangleEqual -> ⊴ - new Transition (4149, 4150), // &LeftUpDownVector -> ⥑ - new Transition (4159, 4160), // &LeftUpTeeVector -> ⥠ - new Transition (4166, 4167), // &LeftUpVector -> ↿ - new Transition (4170, 4171), // &LeftUpVectorBar -> ⥘ - new Transition (4177, 4178), // &LeftVector -> ↼ - new Transition (4181, 4182), // &LeftVectorBar -> ⥒ - new Transition (4183, 4184), // &lEg -> ⪋ - new Transition (4185, 4186), // &leg -> ⋚ - new Transition (4187, 4188), // &leq -> ≤ - new Transition (4189, 4190), // &leqq -> ≦ - new Transition (4195, 4196), // &leqslant -> ⩽ - new Transition (4197, 4198), // &les -> ⩽ - new Transition (4200, 4201), // &lescc -> ⪨ - new Transition (4204, 4205), // &lesdot -> ⩿ - new Transition (4206, 4207), // &lesdoto -> ⪁ - new Transition (4208, 4209), // &lesdotor -> ⪃ - new Transition (4210, 4211), // &lesg -> ⋚︀ - new Transition (4213, 4214), // &lesges -> ⪓ - new Transition (4221, 4222), // &lessapprox -> ⪅ - new Transition (4225, 4226), // &lessdot -> ⋖ - new Transition (4231, 4232), // &lesseqgtr -> ⋚ - new Transition (4236, 4237), // &lesseqqgtr -> ⪋ - new Transition (4251, 4252), // &LessEqualGreater -> ⋚ - new Transition (4261, 4262), // &LessFullEqual -> ≦ - new Transition (4269, 4270), // &LessGreater -> ≶ - new Transition (4273, 4274), // &lessgtr -> ≶ - new Transition (4278, 4279), // &LessLess -> ⪡ - new Transition (4282, 4283), // &lesssim -> ≲ - new Transition (4293, 4294), // &LessSlantEqual -> ⩽ - new Transition (4299, 4300), // &LessTilde -> ≲ - new Transition (4305, 4306), // &lfisht -> ⥼ - new Transition (4310, 4311), // &lfloor -> ⌊ - new Transition (4313, 4314), // &Lfr -> 𝔏 - new Transition (4315, 4316), // &lfr -> 𝔩 - new Transition (4317, 4318), // &lg -> ≶ - new Transition (4319, 4320), // &lgE -> ⪑ - new Transition (4323, 4324), // &lHar -> ⥢ - new Transition (4328, 4329), // &lhard -> ↽ - new Transition (4330, 4331), // &lharu -> ↼ - new Transition (4332, 4333), // &lharul -> ⥪ - new Transition (4336, 4337), // &lhblk -> ▄ - new Transition (4340, 4341), // &LJcy -> Љ - new Transition (4344, 4345), // &ljcy -> љ - new Transition (4346, 4347), // &Ll -> ⋘ - new Transition (4348, 4349), // &ll -> ≪ - new Transition (4352, 4353), // &llarr -> ⇇ - new Transition (4359, 4360), // &llcorner -> ⌞ - new Transition (4368, 4369), // &Lleftarrow -> ⇚ - new Transition (4373, 4374), // &llhard -> ⥫ - new Transition (4377, 4378), // &lltri -> ◺ - new Transition (4383, 4384), // &Lmidot -> Ŀ - new Transition (4389, 4390), // &lmidot -> ŀ - new Transition (4394, 4395), // &lmoust -> ⎰ - new Transition (4399, 4400), // &lmoustache -> ⎰ - new Transition (4403, 4404), // &lnap -> ⪉ - new Transition (4408, 4409), // &lnapprox -> ⪉ - new Transition (4410, 4411), // &lnE -> ≨ - new Transition (4412, 4413), // &lne -> ⪇ - new Transition (4414, 4415), // &lneq -> ⪇ - new Transition (4416, 4417), // &lneqq -> ≨ - new Transition (4420, 4421), // &lnsim -> ⋦ - new Transition (4425, 4426), // &loang -> ⟬ - new Transition (4428, 4429), // &loarr -> ⇽ - new Transition (4432, 4433), // &lobrk -> ⟦ - new Transition (4445, 4446), // &LongLeftArrow -> ⟵ - new Transition (4455, 4456), // &Longleftarrow -> ⟸ - new Transition (4467, 4468), // &longleftarrow -> ⟵ - new Transition (4478, 4479), // &LongLeftRightArrow -> ⟷ - new Transition (4489, 4490), // &Longleftrightarrow -> ⟺ - new Transition (4500, 4501), // &longleftrightarrow -> ⟷ - new Transition (4507, 4508), // &longmapsto -> ⟼ - new Transition (4518, 4519), // &LongRightArrow -> ⟶ - new Transition (4529, 4530), // &Longrightarrow -> ⟹ - new Transition (4540, 4541), // &longrightarrow -> ⟶ - new Transition (4552, 4553), // &looparrowleft -> ↫ - new Transition (4558, 4559), // &looparrowright -> ↬ - new Transition (4562, 4563), // &lopar -> ⦅ - new Transition (4565, 4566), // &Lopf -> 𝕃 - new Transition (4567, 4568), // &lopf -> 𝕝 - new Transition (4571, 4572), // &loplus -> ⨭ - new Transition (4577, 4578), // &lotimes -> ⨴ - new Transition (4582, 4583), // &lowast -> ∗ - new Transition (4586, 4587), // &lowbar -> _ - new Transition (4599, 4600), // &LowerLeftArrow -> ↙ - new Transition (4610, 4611), // &LowerRightArrow -> ↘ - new Transition (4612, 4613), // &loz -> ◊ - new Transition (4617, 4618), // &lozenge -> ◊ - new Transition (4619, 4620), // &lozf -> ⧫ - new Transition (4623, 4624), // &lpar -> ( - new Transition (4626, 4627), // &lparlt -> ⦓ - new Transition (4631, 4632), // &lrarr -> ⇆ - new Transition (4638, 4639), // &lrcorner -> ⌟ - new Transition (4642, 4643), // &lrhar -> ⇋ - new Transition (4644, 4645), // &lrhard -> ⥭ - new Transition (4646, 4647), // &lrm -> ‎ - new Transition (4650, 4651), // &lrtri -> ⊿ - new Transition (4656, 4657), // &lsaquo -> ‹ - new Transition (4660, 4661), // &Lscr -> ℒ - new Transition (4663, 4664), // &lscr -> 𝓁 - new Transition (4665, 4666), // &Lsh -> ↰ - new Transition (4667, 4668), // &lsh -> ↰ - new Transition (4670, 4671), // &lsim -> ≲ - new Transition (4672, 4673), // &lsime -> ⪍ - new Transition (4674, 4675), // &lsimg -> ⪏ - new Transition (4677, 4678), // &lsqb -> [ - new Transition (4680, 4681), // &lsquo -> ‘ - new Transition (4682, 4683), // &lsquor -> ‚ - new Transition (4687, 4688), // &Lstrok -> Ł - new Transition (4692, 4693), // &lstrok -> ł - new Transition (4694, 4695), // < -> < - new Transition (4696, 4697), // &Lt -> ≪ - new Transition (4698, 4699), // < -> < - new Transition (4701, 4702), // <cc -> ⪦ - new Transition (4704, 4705), // <cir -> ⩹ - new Transition (4708, 4709), // <dot -> ⋖ - new Transition (4713, 4714), // <hree -> ⋋ - new Transition (4718, 4719), // <imes -> ⋉ - new Transition (4723, 4724), // <larr -> ⥶ - new Transition (4729, 4730), // <quest -> ⩻ - new Transition (4732, 4733), // <ri -> ◃ - new Transition (4734, 4735), // <rie -> ⊴ - new Transition (4736, 4737), // <rif -> ◂ - new Transition (4740, 4741), // <rPar -> ⦖ - new Transition (4748, 4749), // &lurdshar -> ⥊ - new Transition (4753, 4754), // &luruhar -> ⥦ - new Transition (4762, 4763), // &lvertneqq -> ≨︀ - new Transition (4765, 4766), // &lvnE -> ≨︀ - new Transition (4770, 4771), // ¯ -> ¯ - new Transition (4773, 4774), // &male -> ♂ - new Transition (4775, 4776), // &malt -> ✠ - new Transition (4779, 4780), // &maltese -> ✠ - new Transition (4783, 4784), // &Map -> ⤅ - new Transition (4785, 4786), // &map -> ↦ - new Transition (4789, 4790), // &mapsto -> ↦ - new Transition (4794, 4795), // &mapstodown -> ↧ - new Transition (4799, 4800), // &mapstoleft -> ↤ - new Transition (4802, 4803), // &mapstoup -> ↥ - new Transition (4807, 4808), // &marker -> ▮ - new Transition (4813, 4814), // &mcomma -> ⨩ - new Transition (4816, 4817), // &Mcy -> М - new Transition (4818, 4819), // &mcy -> м - new Transition (4823, 4824), // &mdash -> — - new Transition (4828, 4829), // &mDDot -> ∺ - new Transition (4841, 4842), // &measuredangle -> ∡ - new Transition (4852, 4853), // &MediumSpace ->   - new Transition (4860, 4861), // &Mellintrf -> ℳ - new Transition (4863, 4864), // &Mfr -> 𝔐 - new Transition (4866, 4867), // &mfr -> 𝔪 - new Transition (4869, 4870), // &mho -> ℧ - new Transition (4874, 4875), // µ -> µ - new Transition (4876, 4877), // &mid -> ∣ - new Transition (4880, 4881), // &midast -> * - new Transition (4884, 4885), // &midcir -> ⫰ - new Transition (4888, 4889), // · -> · - new Transition (4892, 4893), // &minus -> − - new Transition (4894, 4895), // &minusb -> ⊟ - new Transition (4896, 4897), // &minusd -> ∸ - new Transition (4898, 4899), // &minusdu -> ⨪ - new Transition (4907, 4908), // &MinusPlus -> ∓ - new Transition (4911, 4912), // &mlcp -> ⫛ - new Transition (4914, 4915), // &mldr -> … - new Transition (4920, 4921), // &mnplus -> ∓ - new Transition (4926, 4927), // &models -> ⊧ - new Transition (4930, 4931), // &Mopf -> 𝕄 - new Transition (4933, 4934), // &mopf -> 𝕞 - new Transition (4935, 4936), // &mp -> ∓ - new Transition (4939, 4940), // &Mscr -> ℳ - new Transition (4943, 4944), // &mscr -> 𝓂 - new Transition (4948, 4949), // &mstpos -> ∾ - new Transition (4950, 4951), // &Mu -> Μ - new Transition (4952, 4953), // &mu -> μ - new Transition (4959, 4960), // &multimap -> ⊸ - new Transition (4963, 4964), // &mumap -> ⊸ - new Transition (4969, 4970), // &nabla -> ∇ - new Transition (4976, 4977), // &Nacute -> Ń - new Transition (4981, 4982), // &nacute -> ń - new Transition (4984, 4985), // &nang -> ∠⃒ - new Transition (4986, 4987), // &nap -> ≉ - new Transition (4988, 4989), // &napE -> ⩰̸ - new Transition (4991, 4992), // &napid -> ≋̸ - new Transition (4994, 4995), // &napos -> ʼn - new Transition (4999, 5000), // &napprox -> ≉ - new Transition (5003, 5004), // &natur -> ♮ - new Transition (5006, 5007), // &natural -> ♮ - new Transition (5008, 5009), // &naturals -> ℕ - new Transition (5012, 5013), //   ->   - new Transition (5016, 5017), // &nbump -> ≎̸ - new Transition (5018, 5019), // &nbumpe -> ≏̸ - new Transition (5022, 5023), // &ncap -> ⩃ - new Transition (5028, 5029), // &Ncaron -> Ň - new Transition (5032, 5033), // &ncaron -> ň - new Transition (5037, 5038), // &Ncedil -> Ņ - new Transition (5042, 5043), // &ncedil -> ņ - new Transition (5046, 5047), // &ncong -> ≇ - new Transition (5050, 5051), // &ncongdot -> ⩭̸ - new Transition (5053, 5054), // &ncup -> ⩂ - new Transition (5055, 5056), // &Ncy -> Н - new Transition (5057, 5058), // &ncy -> н - new Transition (5062, 5063), // &ndash -> – - new Transition (5064, 5065), // &ne -> ≠ - new Transition (5069, 5070), // &nearhk -> ⤤ - new Transition (5073, 5074), // &neArr -> ⇗ - new Transition (5075, 5076), // &nearr -> ↗ - new Transition (5078, 5079), // &nearrow -> ↗ - new Transition (5082, 5083), // &nedot -> ≐̸ - new Transition (5101, 5102), // &NegativeMediumSpace -> ​ - new Transition (5112, 5113), // &NegativeThickSpace -> ​ - new Transition (5119, 5120), // &NegativeThinSpace -> ​ - new Transition (5133, 5134), // &NegativeVeryThinSpace -> ​ - new Transition (5138, 5139), // &nequiv -> ≢ - new Transition (5143, 5144), // &nesear -> ⤨ - new Transition (5146, 5147), // &nesim -> ≂̸ - new Transition (5165, 5166), // &NestedGreaterGreater -> ≫ - new Transition (5174, 5175), // &NestedLessLess -> ≪ - new Transition (5180, 5181), // &NewLine -> - new Transition (5185, 5186), // &nexist -> ∄ - new Transition (5187, 5188), // &nexists -> ∄ - new Transition (5190, 5191), // &Nfr -> 𝔑 - new Transition (5193, 5194), // &nfr -> 𝔫 - new Transition (5196, 5197), // &ngE -> ≧̸ - new Transition (5198, 5199), // &nge -> ≱ - new Transition (5200, 5201), // &ngeq -> ≱ - new Transition (5202, 5203), // &ngeqq -> ≧̸ - new Transition (5208, 5209), // &ngeqslant -> ⩾̸ - new Transition (5210, 5211), // &nges -> ⩾̸ - new Transition (5213, 5214), // &nGg -> ⋙̸ - new Transition (5217, 5218), // &ngsim -> ≵ - new Transition (5219, 5220), // &nGt -> ≫⃒ - new Transition (5221, 5222), // &ngt -> ≯ - new Transition (5223, 5224), // &ngtr -> ≯ - new Transition (5225, 5226), // &nGtv -> ≫̸ - new Transition (5230, 5231), // &nhArr -> ⇎ - new Transition (5234, 5235), // &nharr -> ↮ - new Transition (5238, 5239), // &nhpar -> ⫲ - new Transition (5240, 5241), // &ni -> ∋ - new Transition (5242, 5243), // &nis -> ⋼ - new Transition (5244, 5245), // &nisd -> ⋺ - new Transition (5246, 5247), // &niv -> ∋ - new Transition (5250, 5251), // &NJcy -> Њ - new Transition (5254, 5255), // &njcy -> њ - new Transition (5259, 5260), // &nlArr -> ⇍ - new Transition (5263, 5264), // &nlarr -> ↚ - new Transition (5266, 5267), // &nldr -> ‥ - new Transition (5268, 5269), // &nlE -> ≦̸ - new Transition (5270, 5271), // &nle -> ≰ - new Transition (5280, 5281), // &nLeftarrow -> ⇍ - new Transition (5288, 5289), // &nleftarrow -> ↚ - new Transition (5299, 5300), // &nLeftrightarrow -> ⇎ - new Transition (5310, 5311), // &nleftrightarrow -> ↮ - new Transition (5312, 5313), // &nleq -> ≰ - new Transition (5314, 5315), // &nleqq -> ≦̸ - new Transition (5320, 5321), // &nleqslant -> ⩽̸ - new Transition (5322, 5323), // &nles -> ⩽̸ - new Transition (5324, 5325), // &nless -> ≮ - new Transition (5326, 5327), // &nLl -> ⋘̸ - new Transition (5330, 5331), // &nlsim -> ≴ - new Transition (5332, 5333), // &nLt -> ≪⃒ - new Transition (5334, 5335), // &nlt -> ≮ - new Transition (5337, 5338), // &nltri -> ⋪ - new Transition (5339, 5340), // &nltrie -> ⋬ - new Transition (5341, 5342), // &nLtv -> ≪̸ - new Transition (5345, 5346), // &nmid -> ∤ - new Transition (5352, 5353), // &NoBreak -> ⁠ - new Transition (5367, 5368), // &NonBreakingSpace ->   - new Transition (5370, 5371), // &Nopf -> ℕ - new Transition (5374, 5375), // &nopf -> 𝕟 - new Transition (5376, 5377), // &Not -> ⫬ - new Transition (5378, 5379), // ¬ -> ¬ - new Transition (5388, 5389), // &NotCongruent -> ≢ - new Transition (5394, 5395), // &NotCupCap -> ≭ - new Transition (5412, 5413), // &NotDoubleVerticalBar -> ∦ - new Transition (5420, 5421), // &NotElement -> ∉ - new Transition (5425, 5426), // &NotEqual -> ≠ - new Transition (5431, 5432), // &NotEqualTilde -> ≂̸ - new Transition (5437, 5438), // &NotExists -> ∄ - new Transition (5445, 5446), // &NotGreater -> ≯ - new Transition (5451, 5452), // &NotGreaterEqual -> ≱ - new Transition (5461, 5462), // &NotGreaterFullEqual -> ≧̸ - new Transition (5469, 5470), // &NotGreaterGreater -> ≫̸ - new Transition (5474, 5475), // &NotGreaterLess -> ≹ - new Transition (5485, 5486), // &NotGreaterSlantEqual -> ⩾̸ - new Transition (5491, 5492), // &NotGreaterTilde -> ≵ - new Transition (5504, 5505), // &NotHumpDownHump -> ≎̸ - new Transition (5510, 5511), // &NotHumpEqual -> ≏̸ - new Transition (5513, 5514), // ¬in -> ∉ - new Transition (5517, 5518), // ¬indot -> ⋵̸ - new Transition (5519, 5520), // ¬inE -> ⋹̸ - new Transition (5522, 5523), // ¬inva -> ∉ - new Transition (5524, 5525), // ¬invb -> ⋷ - new Transition (5526, 5527), // ¬invc -> ⋶ - new Transition (5539, 5540), // &NotLeftTriangle -> ⋪ - new Transition (5543, 5544), // &NotLeftTriangleBar -> ⧏̸ - new Transition (5549, 5550), // &NotLeftTriangleEqual -> ⋬ - new Transition (5552, 5553), // &NotLess -> ≮ - new Transition (5558, 5559), // &NotLessEqual -> ≰ - new Transition (5566, 5567), // &NotLessGreater -> ≸ - new Transition (5571, 5572), // &NotLessLess -> ≪̸ - new Transition (5582, 5583), // &NotLessSlantEqual -> ⩽̸ - new Transition (5588, 5589), // &NotLessTilde -> ≴ - new Transition (5609, 5610), // &NotNestedGreaterGreater -> ⪢̸ - new Transition (5618, 5619), // &NotNestedLessLess -> ⪡̸ - new Transition (5621, 5622), // ¬ni -> ∌ - new Transition (5624, 5625), // ¬niva -> ∌ - new Transition (5626, 5627), // ¬nivb -> ⋾ - new Transition (5628, 5629), // ¬nivc -> ⋽ - new Transition (5637, 5638), // &NotPrecedes -> ⊀ - new Transition (5643, 5644), // &NotPrecedesEqual -> ⪯̸ - new Transition (5654, 5655), // &NotPrecedesSlantEqual -> ⋠ - new Transition (5669, 5670), // &NotReverseElement -> ∌ - new Transition (5682, 5683), // &NotRightTriangle -> ⋫ - new Transition (5686, 5687), // &NotRightTriangleBar -> ⧐̸ - new Transition (5692, 5693), // &NotRightTriangleEqual -> ⋭ - new Transition (5705, 5706), // &NotSquareSubset -> ⊏̸ - new Transition (5711, 5712), // &NotSquareSubsetEqual -> ⋢ - new Transition (5718, 5719), // &NotSquareSuperset -> ⊐̸ - new Transition (5724, 5725), // &NotSquareSupersetEqual -> ⋣ - new Transition (5730, 5731), // &NotSubset -> ⊂⃒ - new Transition (5736, 5737), // &NotSubsetEqual -> ⊈ - new Transition (5743, 5744), // &NotSucceeds -> ⊁ - new Transition (5749, 5750), // &NotSucceedsEqual -> ⪰̸ - new Transition (5760, 5761), // &NotSucceedsSlantEqual -> ⋡ - new Transition (5766, 5767), // &NotSucceedsTilde -> ≿̸ - new Transition (5773, 5774), // &NotSuperset -> ⊃⃒ - new Transition (5779, 5780), // &NotSupersetEqual -> ⊉ - new Transition (5785, 5786), // &NotTilde -> ≁ - new Transition (5791, 5792), // &NotTildeEqual -> ≄ - new Transition (5801, 5802), // &NotTildeFullEqual -> ≇ - new Transition (5807, 5808), // &NotTildeTilde -> ≉ - new Transition (5819, 5820), // &NotVerticalBar -> ∤ - new Transition (5823, 5824), // &npar -> ∦ - new Transition (5829, 5830), // &nparallel -> ∦ - new Transition (5832, 5833), // &nparsl -> ⫽⃥ - new Transition (5834, 5835), // &npart -> ∂̸ - new Transition (5840, 5841), // &npolint -> ⨔ - new Transition (5842, 5843), // &npr -> ⊀ - new Transition (5846, 5847), // &nprcue -> ⋠ - new Transition (5848, 5849), // &npre -> ⪯̸ - new Transition (5850, 5851), // &nprec -> ⊀ - new Transition (5853, 5854), // &npreceq -> ⪯̸ - new Transition (5858, 5859), // &nrArr -> ⇏ - new Transition (5862, 5863), // &nrarr -> ↛ - new Transition (5864, 5865), // &nrarrc -> ⤳̸ - new Transition (5866, 5867), // &nrarrw -> ↝̸ - new Transition (5877, 5878), // &nRightarrow -> ⇏ - new Transition (5887, 5888), // &nrightarrow -> ↛ - new Transition (5891, 5892), // &nrtri -> ⋫ - new Transition (5893, 5894), // &nrtrie -> ⋭ - new Transition (5896, 5897), // &nsc -> ⊁ - new Transition (5900, 5901), // &nsccue -> ⋡ - new Transition (5902, 5903), // &nsce -> ⪰̸ - new Transition (5906, 5907), // &Nscr -> 𝒩 - new Transition (5908, 5909), // &nscr -> 𝓃 - new Transition (5916, 5917), // &nshortmid -> ∤ - new Transition (5925, 5926), // &nshortparallel -> ∦ - new Transition (5928, 5929), // &nsim -> ≁ - new Transition (5930, 5931), // &nsime -> ≄ - new Transition (5932, 5933), // &nsimeq -> ≄ - new Transition (5936, 5937), // &nsmid -> ∤ - new Transition (5940, 5941), // &nspar -> ∦ - new Transition (5946, 5947), // &nsqsube -> ⋢ - new Transition (5949, 5950), // &nsqsupe -> ⋣ - new Transition (5952, 5953), // &nsub -> ⊄ - new Transition (5954, 5955), // &nsubE -> ⫅̸ - new Transition (5956, 5957), // &nsube -> ⊈ - new Transition (5960, 5961), // &nsubset -> ⊂⃒ - new Transition (5963, 5964), // &nsubseteq -> ⊈ - new Transition (5965, 5966), // &nsubseteqq -> ⫅̸ - new Transition (5968, 5969), // &nsucc -> ⊁ - new Transition (5971, 5972), // &nsucceq -> ⪰̸ - new Transition (5973, 5974), // &nsup -> ⊅ - new Transition (5975, 5976), // &nsupE -> ⫆̸ - new Transition (5977, 5978), // &nsupe -> ⊉ - new Transition (5981, 5982), // &nsupset -> ⊃⃒ - new Transition (5984, 5985), // &nsupseteq -> ⊉ - new Transition (5986, 5987), // &nsupseteqq -> ⫆̸ - new Transition (5990, 5991), // &ntgl -> ≹ - new Transition (5996, 5997), // Ñ -> Ñ - new Transition (6001, 6002), // ñ -> ñ - new Transition (6004, 6005), // &ntlg -> ≸ - new Transition (6016, 6017), // &ntriangleleft -> ⋪ - new Transition (6019, 6020), // &ntrianglelefteq -> ⋬ - new Transition (6025, 6026), // &ntriangleright -> ⋫ - new Transition (6028, 6029), // &ntrianglerighteq -> ⋭ - new Transition (6030, 6031), // &Nu -> Ν - new Transition (6032, 6033), // &nu -> ν - new Transition (6034, 6035), // &num -> # - new Transition (6038, 6039), // &numero -> № - new Transition (6041, 6042), // &numsp ->   - new Transition (6045, 6046), // &nvap -> ≍⃒ - new Transition (6051, 6052), // &nVDash -> ⊯ - new Transition (6056, 6057), // &nVdash -> ⊮ - new Transition (6061, 6062), // &nvDash -> ⊭ - new Transition (6066, 6067), // &nvdash -> ⊬ - new Transition (6069, 6070), // &nvge -> ≥⃒ - new Transition (6071, 6072), // &nvgt -> >⃒ - new Transition (6076, 6077), // &nvHarr -> ⤄ - new Transition (6082, 6083), // &nvinfin -> ⧞ - new Transition (6087, 6088), // &nvlArr -> ⤂ - new Transition (6089, 6090), // &nvle -> ≤⃒ - new Transition (6091, 6092), // &nvlt -> <⃒ - new Transition (6095, 6096), // &nvltrie -> ⊴⃒ - new Transition (6100, 6101), // &nvrArr -> ⤃ - new Transition (6105, 6106), // &nvrtrie -> ⊵⃒ - new Transition (6109, 6110), // &nvsim -> ∼⃒ - new Transition (6115, 6116), // &nwarhk -> ⤣ - new Transition (6119, 6120), // &nwArr -> ⇖ - new Transition (6121, 6122), // &nwarr -> ↖ - new Transition (6124, 6125), // &nwarrow -> ↖ - new Transition (6129, 6130), // &nwnear -> ⤧ - new Transition (6136, 6137), // Ó -> Ó - new Transition (6143, 6144), // ó -> ó - new Transition (6146, 6147), // &oast -> ⊛ - new Transition (6150, 6151), // &ocir -> ⊚ - new Transition (6155, 6156), // Ô -> Ô - new Transition (6157, 6158), // ô -> ô - new Transition (6159, 6160), // &Ocy -> О - new Transition (6161, 6162), // &ocy -> о - new Transition (6166, 6167), // &odash -> ⊝ - new Transition (6172, 6173), // &Odblac -> Ő - new Transition (6177, 6178), // &odblac -> ő - new Transition (6180, 6181), // &odiv -> ⨸ - new Transition (6183, 6184), // &odot -> ⊙ - new Transition (6188, 6189), // &odsold -> ⦼ - new Transition (6193, 6194), // &OElig -> Œ - new Transition (6198, 6199), // &oelig -> œ - new Transition (6203, 6204), // &ofcir -> ⦿ - new Transition (6206, 6207), // &Ofr -> 𝔒 - new Transition (6208, 6209), // &ofr -> 𝔬 - new Transition (6212, 6213), // &ogon -> ˛ - new Transition (6218, 6219), // Ò -> Ò - new Transition (6223, 6224), // ò -> ò - new Transition (6225, 6226), // &ogt -> ⧁ - new Transition (6230, 6231), // &ohbar -> ⦵ - new Transition (6232, 6233), // &ohm -> Ω - new Transition (6236, 6237), // &oint -> ∮ - new Transition (6241, 6242), // &olarr -> ↺ - new Transition (6245, 6246), // &olcir -> ⦾ - new Transition (6250, 6251), // &olcross -> ⦻ - new Transition (6254, 6255), // &oline -> ‾ - new Transition (6256, 6257), // &olt -> ⧀ - new Transition (6261, 6262), // &Omacr -> Ō - new Transition (6266, 6267), // &omacr -> ō - new Transition (6270, 6271), // &Omega -> Ω - new Transition (6274, 6275), // &omega -> ω - new Transition (6280, 6281), // &Omicron -> Ο - new Transition (6286, 6287), // &omicron -> ο - new Transition (6288, 6289), // &omid -> ⦶ - new Transition (6292, 6293), // &ominus -> ⊖ - new Transition (6296, 6297), // &Oopf -> 𝕆 - new Transition (6300, 6301), // &oopf -> 𝕠 - new Transition (6304, 6305), // &opar -> ⦷ - new Transition (6324, 6325), // &OpenCurlyDoubleQuote -> “ - new Transition (6330, 6331), // &OpenCurlyQuote -> ‘ - new Transition (6334, 6335), // &operp -> ⦹ - new Transition (6338, 6339), // &oplus -> ⊕ - new Transition (6340, 6341), // &Or -> ⩔ - new Transition (6342, 6343), // &or -> ∨ - new Transition (6346, 6347), // &orarr -> ↻ - new Transition (6348, 6349), // &ord -> ⩝ - new Transition (6351, 6352), // &order -> ℴ - new Transition (6354, 6355), // &orderof -> ℴ - new Transition (6356, 6357), // ª -> ª - new Transition (6358, 6359), // º -> º - new Transition (6363, 6364), // &origof -> ⊶ - new Transition (6366, 6367), // &oror -> ⩖ - new Transition (6372, 6373), // &orslope -> ⩗ - new Transition (6374, 6375), // &orv -> ⩛ - new Transition (6376, 6377), // &oS -> Ⓢ - new Transition (6380, 6381), // &Oscr -> 𝒪 - new Transition (6384, 6385), // &oscr -> ℴ - new Transition (6389, 6390), // Ø -> Ø - new Transition (6394, 6395), // ø -> ø - new Transition (6397, 6398), // &osol -> ⊘ - new Transition (6403, 6404), // Õ -> Õ - new Transition (6409, 6410), // õ -> õ - new Transition (6413, 6414), // &Otimes -> ⨷ - new Transition (6417, 6418), // &otimes -> ⊗ - new Transition (6420, 6421), // &otimesas -> ⨶ - new Transition (6424, 6425), // Ö -> Ö - new Transition (6428, 6429), // ö -> ö - new Transition (6433, 6434), // &ovbar -> ⌽ - new Transition (6440, 6441), // &OverBar -> ‾ - new Transition (6445, 6446), // &OverBrace -> ⏞ - new Transition (6449, 6450), // &OverBracket -> ⎴ - new Transition (6461, 6462), // &OverParenthesis -> ⏜ - new Transition (6465, 6466), // &par -> ∥ - new Transition (6467, 6468), // ¶ -> ¶ - new Transition (6472, 6473), // ¶llel -> ∥ - new Transition (6476, 6477), // &parsim -> ⫳ - new Transition (6478, 6479), // &parsl -> ⫽ - new Transition (6480, 6481), // &part -> ∂ - new Transition (6489, 6490), // &PartialD -> ∂ - new Transition (6492, 6493), // &Pcy -> П - new Transition (6495, 6496), // &pcy -> п - new Transition (6501, 6502), // &percnt -> % - new Transition (6505, 6506), // &period -> . - new Transition (6509, 6510), // &permil -> ‰ - new Transition (6511, 6512), // &perp -> ⊥ - new Transition (6516, 6517), // &pertenk -> ‱ - new Transition (6519, 6520), // &Pfr -> 𝔓 - new Transition (6522, 6523), // &pfr -> 𝔭 - new Transition (6525, 6526), // &Phi -> Φ - new Transition (6528, 6529), // &phi -> φ - new Transition (6530, 6531), // &phiv -> ϕ - new Transition (6535, 6536), // &phmmat -> ℳ - new Transition (6539, 6540), // &phone -> ☎ - new Transition (6541, 6542), // &Pi -> Π - new Transition (6543, 6544), // &pi -> π - new Transition (6551, 6552), // &pitchfork -> ⋔ - new Transition (6553, 6554), // &piv -> ϖ - new Transition (6559, 6560), // &planck -> ℏ - new Transition (6561, 6562), // &planckh -> ℎ - new Transition (6564, 6565), // &plankv -> ℏ - new Transition (6567, 6568), // &plus -> + - new Transition (6572, 6573), // &plusacir -> ⨣ - new Transition (6574, 6575), // &plusb -> ⊞ - new Transition (6578, 6579), // &pluscir -> ⨢ - new Transition (6581, 6582), // &plusdo -> ∔ - new Transition (6583, 6584), // &plusdu -> ⨥ - new Transition (6585, 6586), // &pluse -> ⩲ - new Transition (6594, 6595), // &PlusMinus -> ± - new Transition (6597, 6598), // ± -> ± - new Transition (6601, 6602), // &plussim -> ⨦ - new Transition (6605, 6606), // &plustwo -> ⨧ - new Transition (6607, 6608), // &pm -> ± - new Transition (6620, 6621), // &Poincareplane -> ℌ - new Transition (6628, 6629), // &pointint -> ⨕ - new Transition (6631, 6632), // &Popf -> ℙ - new Transition (6634, 6635), // &popf -> 𝕡 - new Transition (6638, 6639), // £ -> £ - new Transition (6640, 6641), // &Pr -> ⪻ - new Transition (6642, 6643), // &pr -> ≺ - new Transition (6645, 6646), // &prap -> ⪷ - new Transition (6649, 6650), // &prcue -> ≼ - new Transition (6651, 6652), // &prE -> ⪳ - new Transition (6653, 6654), // &pre -> ⪯ - new Transition (6655, 6656), // &prec -> ≺ - new Transition (6662, 6663), // &precapprox -> ⪷ - new Transition (6670, 6671), // &preccurlyeq -> ≼ - new Transition (6677, 6678), // &Precedes -> ≺ - new Transition (6683, 6684), // &PrecedesEqual -> ⪯ - new Transition (6694, 6695), // &PrecedesSlantEqual -> ≼ - new Transition (6700, 6701), // &PrecedesTilde -> ≾ - new Transition (6703, 6704), // &preceq -> ⪯ - new Transition (6711, 6712), // &precnapprox -> ⪹ - new Transition (6715, 6716), // &precneqq -> ⪵ - new Transition (6719, 6720), // &precnsim -> ⋨ - new Transition (6723, 6724), // &precsim -> ≾ - new Transition (6727, 6728), // &Prime -> ″ - new Transition (6731, 6732), // &prime -> ′ - new Transition (6733, 6734), // &primes -> ℙ - new Transition (6737, 6738), // &prnap -> ⪹ - new Transition (6739, 6740), // &prnE -> ⪵ - new Transition (6743, 6744), // &prnsim -> ⋨ - new Transition (6746, 6747), // &prod -> ∏ - new Transition (6752, 6753), // &Product -> ∏ - new Transition (6758, 6759), // &profalar -> ⌮ - new Transition (6763, 6764), // &profline -> ⌒ - new Transition (6768, 6769), // &profsurf -> ⌓ - new Transition (6770, 6771), // &prop -> ∝ - new Transition (6778, 6779), // &Proportion -> ∷ - new Transition (6781, 6782), // &Proportional -> ∝ - new Transition (6784, 6785), // &propto -> ∝ - new Transition (6788, 6789), // &prsim -> ≾ - new Transition (6793, 6794), // &prurel -> ⊰ - new Transition (6797, 6798), // &Pscr -> 𝒫 - new Transition (6801, 6802), // &pscr -> 𝓅 - new Transition (6803, 6804), // &Psi -> Ψ - new Transition (6805, 6806), // &psi -> ψ - new Transition (6811, 6812), // &puncsp ->   - new Transition (6815, 6816), // &Qfr -> 𝔔 - new Transition (6819, 6820), // &qfr -> 𝔮 - new Transition (6823, 6824), // &qint -> ⨌ - new Transition (6827, 6828), // &Qopf -> ℚ - new Transition (6831, 6832), // &qopf -> 𝕢 - new Transition (6837, 6838), // &qprime -> ⁗ - new Transition (6841, 6842), // &Qscr -> 𝒬 - new Transition (6845, 6846), // &qscr -> 𝓆 - new Transition (6856, 6857), // &quaternions -> ℍ - new Transition (6860, 6861), // &quatint -> ⨖ - new Transition (6864, 6865), // &quest -> ? - new Transition (6867, 6868), // &questeq -> ≟ - new Transition (6871, 6872), // " -> " - new Transition (6874, 6875), // " -> " - new Transition (6880, 6881), // &rAarr -> ⇛ - new Transition (6884, 6885), // &race -> ∽̱ - new Transition (6891, 6892), // &Racute -> Ŕ - new Transition (6895, 6896), // &racute -> ŕ - new Transition (6899, 6900), // &radic -> √ - new Transition (6906, 6907), // &raemptyv -> ⦳ - new Transition (6909, 6910), // &Rang -> ⟫ - new Transition (6912, 6913), // &rang -> ⟩ - new Transition (6914, 6915), // &rangd -> ⦒ - new Transition (6916, 6917), // &range -> ⦥ - new Transition (6919, 6920), // &rangle -> ⟩ - new Transition (6923, 6924), // » -> » - new Transition (6926, 6927), // &Rarr -> ↠ - new Transition (6929, 6930), // &rArr -> ⇒ - new Transition (6932, 6933), // &rarr -> → - new Transition (6935, 6936), // &rarrap -> ⥵ - new Transition (6937, 6938), // &rarrb -> ⇥ - new Transition (6940, 6941), // &rarrbfs -> ⤠ - new Transition (6942, 6943), // &rarrc -> ⤳ - new Transition (6945, 6946), // &rarrfs -> ⤞ - new Transition (6948, 6949), // &rarrhk -> ↪ - new Transition (6951, 6952), // &rarrlp -> ↬ - new Transition (6954, 6955), // &rarrpl -> ⥅ - new Transition (6958, 6959), // &rarrsim -> ⥴ - new Transition (6961, 6962), // &Rarrtl -> ⤖ - new Transition (6964, 6965), // &rarrtl -> ↣ - new Transition (6966, 6967), // &rarrw -> ↝ - new Transition (6971, 6972), // &rAtail -> ⤜ - new Transition (6976, 6977), // &ratail -> ⤚ - new Transition (6979, 6980), // &ratio -> ∶ - new Transition (6984, 6985), // &rationals -> ℚ - new Transition (6989, 6990), // &RBarr -> ⤐ - new Transition (6994, 6995), // &rBarr -> ⤏ - new Transition (6999, 7000), // &rbarr -> ⤍ - new Transition (7003, 7004), // &rbbrk -> ❳ - new Transition (7008, 7009), // &rbrace -> } - new Transition (7010, 7011), // &rbrack -> ] - new Transition (7013, 7014), // &rbrke -> ⦌ - new Transition (7017, 7018), // &rbrksld -> ⦎ - new Transition (7019, 7020), // &rbrkslu -> ⦐ - new Transition (7025, 7026), // &Rcaron -> Ř - new Transition (7031, 7032), // &rcaron -> ř - new Transition (7036, 7037), // &Rcedil -> Ŗ - new Transition (7041, 7042), // &rcedil -> ŗ - new Transition (7044, 7045), // &rceil -> ⌉ - new Transition (7047, 7048), // &rcub -> } - new Transition (7049, 7050), // &Rcy -> Р - new Transition (7051, 7052), // &rcy -> р - new Transition (7055, 7056), // &rdca -> ⤷ - new Transition (7061, 7062), // &rdldhar -> ⥩ - new Transition (7065, 7066), // &rdquo -> ” - new Transition (7067, 7068), // &rdquor -> ” - new Transition (7070, 7071), // &rdsh -> ↳ - new Transition (7072, 7073), // &Re -> ℜ - new Transition (7076, 7077), // &real -> ℜ - new Transition (7080, 7081), // &realine -> ℛ - new Transition (7085, 7086), // &realpart -> ℜ - new Transition (7087, 7088), // &reals -> ℝ - new Transition (7090, 7091), // &rect -> ▭ - new Transition (7093, 7094), // ® -> ® - new Transition (7095, 7096), // ® -> ® - new Transition (7108, 7109), // &ReverseElement -> ∋ - new Transition (7119, 7120), // &ReverseEquilibrium -> ⇋ - new Transition (7133, 7134), // &ReverseUpEquilibrium -> ⥯ - new Transition (7139, 7140), // &rfisht -> ⥽ - new Transition (7144, 7145), // &rfloor -> ⌋ - new Transition (7147, 7148), // &Rfr -> ℜ - new Transition (7149, 7150), // &rfr -> 𝔯 - new Transition (7153, 7154), // &rHar -> ⥤ - new Transition (7158, 7159), // &rhard -> ⇁ - new Transition (7160, 7161), // &rharu -> ⇀ - new Transition (7162, 7163), // &rharul -> ⥬ - new Transition (7165, 7166), // &Rho -> Ρ - new Transition (7167, 7168), // &rho -> ρ - new Transition (7169, 7170), // &rhov -> ϱ - new Transition (7186, 7187), // &RightAngleBracket -> ⟩ - new Transition (7191, 7192), // &RightArrow -> → - new Transition (7197, 7198), // &Rightarrow -> ⇒ - new Transition (7207, 7208), // &rightarrow -> → - new Transition (7211, 7212), // &RightArrowBar -> ⇥ - new Transition (7221, 7222), // &RightArrowLeftArrow -> ⇄ - new Transition (7226, 7227), // &rightarrowtail -> ↣ - new Transition (7234, 7235), // &RightCeiling -> ⌉ - new Transition (7248, 7249), // &RightDoubleBracket -> ⟧ - new Transition (7260, 7261), // &RightDownTeeVector -> ⥝ - new Transition (7267, 7268), // &RightDownVector -> ⇂ - new Transition (7271, 7272), // &RightDownVectorBar -> ⥕ - new Transition (7277, 7278), // &RightFloor -> ⌋ - new Transition (7289, 7290), // &rightharpoondown -> ⇁ - new Transition (7292, 7293), // &rightharpoonup -> ⇀ - new Transition (7303, 7304), // &rightleftarrows -> ⇄ - new Transition (7312, 7313), // &rightleftharpoons -> ⇌ - new Transition (7324, 7325), // &rightrightarrows -> ⇉ - new Transition (7335, 7336), // &rightsquigarrow -> ↝ - new Transition (7339, 7340), // &RightTee -> ⊢ - new Transition (7345, 7346), // &RightTeeArrow -> ↦ - new Transition (7352, 7353), // &RightTeeVector -> ⥛ - new Transition (7363, 7364), // &rightthreetimes -> ⋌ - new Transition (7371, 7372), // &RightTriangle -> ⊳ - new Transition (7375, 7376), // &RightTriangleBar -> ⧐ - new Transition (7381, 7382), // &RightTriangleEqual -> ⊵ - new Transition (7394, 7395), // &RightUpDownVector -> ⥏ - new Transition (7404, 7405), // &RightUpTeeVector -> ⥜ - new Transition (7411, 7412), // &RightUpVector -> ↾ - new Transition (7415, 7416), // &RightUpVectorBar -> ⥔ - new Transition (7422, 7423), // &RightVector -> ⇀ - new Transition (7426, 7427), // &RightVectorBar -> ⥓ - new Transition (7429, 7430), // &ring -> ˚ - new Transition (7440, 7441), // &risingdotseq -> ≓ - new Transition (7445, 7446), // &rlarr -> ⇄ - new Transition (7449, 7450), // &rlhar -> ⇌ - new Transition (7451, 7452), // &rlm -> ‏ - new Transition (7457, 7458), // &rmoust -> ⎱ - new Transition (7462, 7463), // &rmoustache -> ⎱ - new Transition (7467, 7468), // &rnmid -> ⫮ - new Transition (7472, 7473), // &roang -> ⟭ - new Transition (7475, 7476), // &roarr -> ⇾ - new Transition (7479, 7480), // &robrk -> ⟧ - new Transition (7483, 7484), // &ropar -> ⦆ - new Transition (7487, 7488), // &Ropf -> ℝ - new Transition (7489, 7490), // &ropf -> 𝕣 - new Transition (7493, 7494), // &roplus -> ⨮ - new Transition (7499, 7500), // &rotimes -> ⨵ - new Transition (7510, 7511), // &RoundImplies -> ⥰ - new Transition (7514, 7515), // &rpar -> ) - new Transition (7517, 7518), // &rpargt -> ⦔ - new Transition (7524, 7525), // &rppolint -> ⨒ - new Transition (7529, 7530), // &rrarr -> ⇉ - new Transition (7540, 7541), // &Rrightarrow -> ⇛ - new Transition (7546, 7547), // &rsaquo -> › - new Transition (7550, 7551), // &Rscr -> ℛ - new Transition (7553, 7554), // &rscr -> 𝓇 - new Transition (7555, 7556), // &Rsh -> ↱ - new Transition (7557, 7558), // &rsh -> ↱ - new Transition (7560, 7561), // &rsqb -> ] - new Transition (7563, 7564), // &rsquo -> ’ - new Transition (7565, 7566), // &rsquor -> ’ - new Transition (7571, 7572), // &rthree -> ⋌ - new Transition (7576, 7577), // &rtimes -> ⋊ - new Transition (7579, 7580), // &rtri -> ▹ - new Transition (7581, 7582), // &rtrie -> ⊵ - new Transition (7583, 7584), // &rtrif -> ▸ - new Transition (7588, 7589), // &rtriltri -> ⧎ - new Transition (7599, 7600), // &RuleDelayed -> ⧴ - new Transition (7606, 7607), // &ruluhar -> ⥨ - new Transition (7608, 7609), // &rx -> ℞ - new Transition (7615, 7616), // &Sacute -> Ś - new Transition (7622, 7623), // &sacute -> ś - new Transition (7627, 7628), // &sbquo -> ‚ - new Transition (7629, 7630), // &Sc -> ⪼ - new Transition (7631, 7632), // &sc -> ≻ - new Transition (7634, 7635), // &scap -> ⪸ - new Transition (7639, 7640), // &Scaron -> Š - new Transition (7643, 7644), // &scaron -> š - new Transition (7647, 7648), // &sccue -> ≽ - new Transition (7649, 7650), // &scE -> ⪴ - new Transition (7651, 7652), // &sce -> ⪰ - new Transition (7656, 7657), // &Scedil -> Ş - new Transition (7660, 7661), // &scedil -> ş - new Transition (7664, 7665), // &Scirc -> Ŝ - new Transition (7668, 7669), // &scirc -> ŝ - new Transition (7672, 7673), // &scnap -> ⪺ - new Transition (7674, 7675), // &scnE -> ⪶ - new Transition (7678, 7679), // &scnsim -> ⋩ - new Transition (7685, 7686), // &scpolint -> ⨓ - new Transition (7689, 7690), // &scsim -> ≿ - new Transition (7691, 7692), // &Scy -> С - new Transition (7693, 7694), // &scy -> с - new Transition (7697, 7698), // &sdot -> ⋅ - new Transition (7699, 7700), // &sdotb -> ⊡ - new Transition (7701, 7702), // &sdote -> ⩦ - new Transition (7707, 7708), // &searhk -> ⤥ - new Transition (7711, 7712), // &seArr -> ⇘ - new Transition (7713, 7714), // &searr -> ↘ - new Transition (7716, 7717), // &searrow -> ↘ - new Transition (7719, 7720), // § -> § - new Transition (7722, 7723), // &semi -> ; - new Transition (7727, 7728), // &seswar -> ⤩ - new Transition (7734, 7735), // &setminus -> ∖ - new Transition (7736, 7737), // &setmn -> ∖ - new Transition (7739, 7740), // &sext -> ✶ - new Transition (7742, 7743), // &Sfr -> 𝔖 - new Transition (7745, 7746), // &sfr -> 𝔰 - new Transition (7749, 7750), // &sfrown -> ⌢ - new Transition (7754, 7755), // &sharp -> ♯ - new Transition (7760, 7761), // &SHCHcy -> Щ - new Transition (7765, 7766), // &shchcy -> щ - new Transition (7768, 7769), // &SHcy -> Ш - new Transition (7770, 7771), // &shcy -> ш - new Transition (7784, 7785), // &ShortDownArrow -> ↓ - new Transition (7794, 7795), // &ShortLeftArrow -> ← - new Transition (7801, 7802), // &shortmid -> ∣ - new Transition (7810, 7811), // &shortparallel -> ∥ - new Transition (7821, 7822), // &ShortRightArrow -> → - new Transition (7829, 7830), // &ShortUpArrow -> ↑ - new Transition (7831, 7832), // ­ -> ­ - new Transition (7836, 7837), // &Sigma -> Σ - new Transition (7841, 7842), // &sigma -> σ - new Transition (7843, 7844), // &sigmaf -> ς - new Transition (7845, 7846), // &sigmav -> ς - new Transition (7847, 7848), // &sim -> ∼ - new Transition (7851, 7852), // &simdot -> ⩪ - new Transition (7853, 7854), // &sime -> ≃ - new Transition (7855, 7856), // &simeq -> ≃ - new Transition (7857, 7858), // &simg -> ⪞ - new Transition (7859, 7860), // &simgE -> ⪠ - new Transition (7861, 7862), // &siml -> ⪝ - new Transition (7863, 7864), // &simlE -> ⪟ - new Transition (7866, 7867), // &simne -> ≆ - new Transition (7871, 7872), // &simplus -> ⨤ - new Transition (7876, 7877), // &simrarr -> ⥲ - new Transition (7881, 7882), // &slarr -> ← - new Transition (7892, 7893), // &SmallCircle -> ∘ - new Transition (7905, 7906), // &smallsetminus -> ∖ - new Transition (7909, 7910), // &smashp -> ⨳ - new Transition (7916, 7917), // &smeparsl -> ⧤ - new Transition (7919, 7920), // &smid -> ∣ - new Transition (7922, 7923), // &smile -> ⌣ - new Transition (7924, 7925), // &smt -> ⪪ - new Transition (7926, 7927), // &smte -> ⪬ - new Transition (7928, 7929), // &smtes -> ⪬︀ - new Transition (7934, 7935), // &SOFTcy -> Ь - new Transition (7940, 7941), // &softcy -> ь - new Transition (7942, 7943), // &sol -> / - new Transition (7944, 7945), // &solb -> ⧄ - new Transition (7947, 7948), // &solbar -> ⌿ - new Transition (7951, 7952), // &Sopf -> 𝕊 - new Transition (7954, 7955), // &sopf -> 𝕤 - new Transition (7960, 7961), // &spades -> ♠ - new Transition (7964, 7965), // &spadesuit -> ♠ - new Transition (7966, 7967), // &spar -> ∥ - new Transition (7971, 7972), // &sqcap -> ⊓ - new Transition (7973, 7974), // &sqcaps -> ⊓︀ - new Transition (7976, 7977), // &sqcup -> ⊔ - new Transition (7978, 7979), // &sqcups -> ⊔︀ - new Transition (7982, 7983), // &Sqrt -> √ - new Transition (7986, 7987), // &sqsub -> ⊏ - new Transition (7988, 7989), // &sqsube -> ⊑ - new Transition (7992, 7993), // &sqsubset -> ⊏ - new Transition (7995, 7996), // &sqsubseteq -> ⊑ - new Transition (7997, 7998), // &sqsup -> ⊐ - new Transition (7999, 8000), // &sqsupe -> ⊒ - new Transition (8003, 8004), // &sqsupset -> ⊐ - new Transition (8006, 8007), // &sqsupseteq -> ⊒ - new Transition (8008, 8009), // &squ -> □ - new Transition (8013, 8014), // &Square -> □ - new Transition (8017, 8018), // &square -> □ - new Transition (8030, 8031), // &SquareIntersection -> ⊓ - new Transition (8037, 8038), // &SquareSubset -> ⊏ - new Transition (8043, 8044), // &SquareSubsetEqual -> ⊑ - new Transition (8050, 8051), // &SquareSuperset -> ⊐ - new Transition (8056, 8057), // &SquareSupersetEqual -> ⊒ - new Transition (8062, 8063), // &SquareUnion -> ⊔ - new Transition (8064, 8065), // &squarf -> ▪ - new Transition (8066, 8067), // &squf -> ▪ - new Transition (8071, 8072), // &srarr -> → - new Transition (8075, 8076), // &Sscr -> 𝒮 - new Transition (8079, 8080), // &sscr -> 𝓈 - new Transition (8084, 8085), // &ssetmn -> ∖ - new Transition (8089, 8090), // &ssmile -> ⌣ - new Transition (8094, 8095), // &sstarf -> ⋆ - new Transition (8098, 8099), // &Star -> ⋆ - new Transition (8102, 8103), // &star -> ☆ - new Transition (8104, 8105), // &starf -> ★ - new Transition (8118, 8119), // &straightepsilon -> ϵ - new Transition (8122, 8123), // &straightphi -> ϕ - new Transition (8125, 8126), // &strns -> ¯ - new Transition (8128, 8129), // &Sub -> ⋐ - new Transition (8131, 8132), // &sub -> ⊂ - new Transition (8135, 8136), // &subdot -> ⪽ - new Transition (8137, 8138), // &subE -> ⫅ - new Transition (8139, 8140), // &sube -> ⊆ - new Transition (8143, 8144), // &subedot -> ⫃ - new Transition (8148, 8149), // &submult -> ⫁ - new Transition (8151, 8152), // &subnE -> ⫋ - new Transition (8153, 8154), // &subne -> ⊊ - new Transition (8158, 8159), // &subplus -> ⪿ - new Transition (8163, 8164), // &subrarr -> ⥹ - new Transition (8167, 8168), // &Subset -> ⋐ - new Transition (8171, 8172), // &subset -> ⊂ - new Transition (8174, 8175), // &subseteq -> ⊆ - new Transition (8176, 8177), // &subseteqq -> ⫅ - new Transition (8182, 8183), // &SubsetEqual -> ⊆ - new Transition (8186, 8187), // &subsetneq -> ⊊ - new Transition (8188, 8189), // &subsetneqq -> ⫋ - new Transition (8191, 8192), // &subsim -> ⫇ - new Transition (8194, 8195), // &subsub -> ⫕ - new Transition (8196, 8197), // &subsup -> ⫓ - new Transition (8199, 8200), // &succ -> ≻ - new Transition (8206, 8207), // &succapprox -> ⪸ - new Transition (8214, 8215), // &succcurlyeq -> ≽ - new Transition (8221, 8222), // &Succeeds -> ≻ - new Transition (8227, 8228), // &SucceedsEqual -> ⪰ - new Transition (8238, 8239), // &SucceedsSlantEqual -> ≽ - new Transition (8244, 8245), // &SucceedsTilde -> ≿ - new Transition (8247, 8248), // &succeq -> ⪰ - new Transition (8255, 8256), // &succnapprox -> ⪺ - new Transition (8259, 8260), // &succneqq -> ⪶ - new Transition (8263, 8264), // &succnsim -> ⋩ - new Transition (8267, 8268), // &succsim -> ≿ - new Transition (8273, 8274), // &SuchThat -> ∋ - new Transition (8275, 8276), // &Sum -> ∑ - new Transition (8277, 8278), // &sum -> ∑ - new Transition (8280, 8281), // &sung -> ♪ - new Transition (8282, 8283), // &Sup -> ⋑ - new Transition (8284, 8285), // &sup -> ⊃ - new Transition (8286, 8287), // ¹ -> ¹ - new Transition (8288, 8289), // ² -> ² - new Transition (8290, 8291), // ³ -> ³ - new Transition (8294, 8295), // &supdot -> ⪾ - new Transition (8298, 8299), // &supdsub -> ⫘ - new Transition (8300, 8301), // &supE -> ⫆ - new Transition (8302, 8303), // &supe -> ⊇ - new Transition (8306, 8307), // &supedot -> ⫄ - new Transition (8312, 8313), // &Superset -> ⊃ - new Transition (8318, 8319), // &SupersetEqual -> ⊇ - new Transition (8323, 8324), // &suphsol -> ⟉ - new Transition (8326, 8327), // &suphsub -> ⫗ - new Transition (8331, 8332), // &suplarr -> ⥻ - new Transition (8336, 8337), // &supmult -> ⫂ - new Transition (8339, 8340), // &supnE -> ⫌ - new Transition (8341, 8342), // &supne -> ⊋ - new Transition (8346, 8347), // &supplus -> ⫀ - new Transition (8350, 8351), // &Supset -> ⋑ - new Transition (8354, 8355), // &supset -> ⊃ - new Transition (8357, 8358), // &supseteq -> ⊇ - new Transition (8359, 8360), // &supseteqq -> ⫆ - new Transition (8363, 8364), // &supsetneq -> ⊋ - new Transition (8365, 8366), // &supsetneqq -> ⫌ - new Transition (8368, 8369), // &supsim -> ⫈ - new Transition (8371, 8372), // &supsub -> ⫔ - new Transition (8373, 8374), // &supsup -> ⫖ - new Transition (8379, 8380), // &swarhk -> ⤦ - new Transition (8383, 8384), // &swArr -> ⇙ - new Transition (8385, 8386), // &swarr -> ↙ - new Transition (8388, 8389), // &swarrow -> ↙ - new Transition (8393, 8394), // &swnwar -> ⤪ - new Transition (8398, 8399), // ß -> ß - new Transition (8402, 8403), // &Tab -> - new Transition (8409, 8410), // &target -> ⌖ - new Transition (8411, 8412), // &Tau -> Τ - new Transition (8413, 8414), // &tau -> τ - new Transition (8417, 8418), // &tbrk -> ⎴ - new Transition (8423, 8424), // &Tcaron -> Ť - new Transition (8429, 8430), // &tcaron -> ť - new Transition (8434, 8435), // &Tcedil -> Ţ - new Transition (8439, 8440), // &tcedil -> ţ - new Transition (8441, 8442), // &Tcy -> Т - new Transition (8443, 8444), // &tcy -> т - new Transition (8447, 8448), // &tdot -> ⃛ - new Transition (8453, 8454), // &telrec -> ⌕ - new Transition (8456, 8457), // &Tfr -> 𝔗 - new Transition (8459, 8460), // &tfr -> 𝔱 - new Transition (8465, 8466), // &there4 -> ∴ - new Transition (8474, 8475), // &Therefore -> ∴ - new Transition (8479, 8480), // &therefore -> ∴ - new Transition (8482, 8483), // &Theta -> Θ - new Transition (8485, 8486), // &theta -> θ - new Transition (8489, 8490), // &thetasym -> ϑ - new Transition (8491, 8492), // &thetav -> ϑ - new Transition (8501, 8502), // &thickapprox -> ≈ - new Transition (8505, 8506), // &thicksim -> ∼ - new Transition (8514, 8515), // &ThickSpace ->    - new Transition (8518, 8519), // &thinsp ->   - new Transition (8525, 8526), // &ThinSpace ->   - new Transition (8529, 8530), // &thkap -> ≈ - new Transition (8533, 8534), // &thksim -> ∼ - new Transition (8538, 8539), // Þ -> Þ - new Transition (8542, 8543), // þ -> þ - new Transition (8547, 8548), // &Tilde -> ∼ - new Transition (8552, 8553), // &tilde -> ˜ - new Transition (8558, 8559), // &TildeEqual -> ≃ - new Transition (8568, 8569), // &TildeFullEqual -> ≅ - new Transition (8574, 8575), // &TildeTilde -> ≈ - new Transition (8578, 8579), // × -> × - new Transition (8580, 8581), // ×b -> ⊠ - new Transition (8583, 8584), // ×bar -> ⨱ - new Transition (8585, 8586), // ×d -> ⨰ - new Transition (8588, 8589), // &tint -> ∭ - new Transition (8592, 8593), // &toea -> ⤨ - new Transition (8594, 8595), // &top -> ⊤ - new Transition (8598, 8599), // &topbot -> ⌶ - new Transition (8602, 8603), // &topcir -> ⫱ - new Transition (8606, 8607), // &Topf -> 𝕋 - new Transition (8608, 8609), // &topf -> 𝕥 - new Transition (8612, 8613), // &topfork -> ⫚ - new Transition (8615, 8616), // &tosa -> ⤩ - new Transition (8621, 8622), // &tprime -> ‴ - new Transition (8626, 8627), // &TRADE -> ™ - new Transition (8631, 8632), // &trade -> ™ - new Transition (8638, 8639), // &triangle -> ▵ - new Transition (8643, 8644), // &triangledown -> ▿ - new Transition (8648, 8649), // &triangleleft -> ◃ - new Transition (8651, 8652), // &trianglelefteq -> ⊴ - new Transition (8653, 8654), // &triangleq -> ≜ - new Transition (8659, 8660), // &triangleright -> ▹ - new Transition (8662, 8663), // &trianglerighteq -> ⊵ - new Transition (8666, 8667), // &tridot -> ◬ - new Transition (8668, 8669), // &trie -> ≜ - new Transition (8674, 8675), // &triminus -> ⨺ - new Transition (8683, 8684), // &TripleDot -> ⃛ - new Transition (8688, 8689), // &triplus -> ⨹ - new Transition (8691, 8692), // &trisb -> ⧍ - new Transition (8696, 8697), // &tritime -> ⨻ - new Transition (8703, 8704), // &trpezium -> ⏢ - new Transition (8707, 8708), // &Tscr -> 𝒯 - new Transition (8711, 8712), // &tscr -> 𝓉 - new Transition (8715, 8716), // &TScy -> Ц - new Transition (8717, 8718), // &tscy -> ц - new Transition (8721, 8722), // &TSHcy -> Ћ - new Transition (8725, 8726), // &tshcy -> ћ - new Transition (8730, 8731), // &Tstrok -> Ŧ - new Transition (8735, 8736), // &tstrok -> ŧ - new Transition (8740, 8741), // &twixt -> ≬ - new Transition (8755, 8756), // &twoheadleftarrow -> ↞ - new Transition (8766, 8767), // &twoheadrightarrow -> ↠ - new Transition (8773, 8774), // Ú -> Ú - new Transition (8780, 8781), // ú -> ú - new Transition (8783, 8784), // &Uarr -> ↟ - new Transition (8787, 8788), // &uArr -> ⇑ - new Transition (8790, 8791), // &uarr -> ↑ - new Transition (8795, 8796), // &Uarrocir -> ⥉ - new Transition (8800, 8801), // &Ubrcy -> Ў - new Transition (8805, 8806), // &ubrcy -> ў - new Transition (8809, 8810), // &Ubreve -> Ŭ - new Transition (8813, 8814), // &ubreve -> ŭ - new Transition (8818, 8819), // Û -> Û - new Transition (8823, 8824), // û -> û - new Transition (8825, 8826), // &Ucy -> У - new Transition (8827, 8828), // &ucy -> у - new Transition (8832, 8833), // &udarr -> ⇅ - new Transition (8838, 8839), // &Udblac -> Ű - new Transition (8843, 8844), // &udblac -> ű - new Transition (8847, 8848), // &udhar -> ⥮ - new Transition (8853, 8854), // &ufisht -> ⥾ - new Transition (8856, 8857), // &Ufr -> 𝔘 - new Transition (8858, 8859), // &ufr -> 𝔲 - new Transition (8864, 8865), // Ù -> Ù - new Transition (8870, 8871), // ù -> ù - new Transition (8874, 8875), // &uHar -> ⥣ - new Transition (8879, 8880), // &uharl -> ↿ - new Transition (8881, 8882), // &uharr -> ↾ - new Transition (8885, 8886), // &uhblk -> ▀ - new Transition (8891, 8892), // &ulcorn -> ⌜ - new Transition (8894, 8895), // &ulcorner -> ⌜ - new Transition (8898, 8899), // &ulcrop -> ⌏ - new Transition (8902, 8903), // &ultri -> ◸ - new Transition (8907, 8908), // &Umacr -> Ū - new Transition (8912, 8913), // &umacr -> ū - new Transition (8914, 8915), // ¨ -> ¨ - new Transition (8922, 8923), // &UnderBar -> _ - new Transition (8927, 8928), // &UnderBrace -> ⏟ - new Transition (8931, 8932), // &UnderBracket -> ⎵ - new Transition (8943, 8944), // &UnderParenthesis -> ⏝ - new Transition (8947, 8948), // &Union -> ⋃ - new Transition (8952, 8953), // &UnionPlus -> ⊎ - new Transition (8957, 8958), // &Uogon -> Ų - new Transition (8962, 8963), // &uogon -> ų - new Transition (8965, 8966), // &Uopf -> 𝕌 - new Transition (8968, 8969), // &uopf -> 𝕦 - new Transition (8975, 8976), // &UpArrow -> ↑ - new Transition (8981, 8982), // &Uparrow -> ⇑ - new Transition (8988, 8989), // &uparrow -> ↑ - new Transition (8992, 8993), // &UpArrowBar -> ⤒ - new Transition (9002, 9003), // &UpArrowDownArrow -> ⇅ - new Transition (9012, 9013), // &UpDownArrow -> ↕ - new Transition (9022, 9023), // &Updownarrow -> ⇕ - new Transition (9032, 9033), // &updownarrow -> ↕ - new Transition (9044, 9045), // &UpEquilibrium -> ⥮ - new Transition (9056, 9057), // &upharpoonleft -> ↿ - new Transition (9062, 9063), // &upharpoonright -> ↾ - new Transition (9066, 9067), // &uplus -> ⊎ - new Transition (9079, 9080), // &UpperLeftArrow -> ↖ - new Transition (9090, 9091), // &UpperRightArrow -> ↗ - new Transition (9093, 9094), // &Upsi -> ϒ - new Transition (9096, 9097), // &upsi -> υ - new Transition (9098, 9099), // &upsih -> ϒ - new Transition (9102, 9103), // &Upsilon -> Υ - new Transition (9106, 9107), // &upsilon -> υ - new Transition (9110, 9111), // &UpTee -> ⊥ - new Transition (9116, 9117), // &UpTeeArrow -> ↥ - new Transition (9125, 9126), // &upuparrows -> ⇈ - new Transition (9131, 9132), // &urcorn -> ⌝ - new Transition (9134, 9135), // &urcorner -> ⌝ - new Transition (9138, 9139), // &urcrop -> ⌎ - new Transition (9143, 9144), // &Uring -> Ů - new Transition (9147, 9148), // &uring -> ů - new Transition (9151, 9152), // &urtri -> ◹ - new Transition (9155, 9156), // &Uscr -> 𝒰 - new Transition (9159, 9160), // &uscr -> 𝓊 - new Transition (9164, 9165), // &utdot -> ⋰ - new Transition (9170, 9171), // &Utilde -> Ũ - new Transition (9175, 9176), // &utilde -> ũ - new Transition (9178, 9179), // &utri -> ▵ - new Transition (9180, 9181), // &utrif -> ▴ - new Transition (9185, 9186), // &uuarr -> ⇈ - new Transition (9189, 9190), // Ü -> Ü - new Transition (9192, 9193), // ü -> ü - new Transition (9199, 9200), // &uwangle -> ⦧ - new Transition (9206, 9207), // &vangrt -> ⦜ - new Transition (9215, 9216), // &varepsilon -> ϵ - new Transition (9221, 9222), // &varkappa -> ϰ - new Transition (9229, 9230), // &varnothing -> ∅ - new Transition (9233, 9234), // &varphi -> ϕ - new Transition (9235, 9236), // &varpi -> ϖ - new Transition (9241, 9242), // &varpropto -> ∝ - new Transition (9245, 9246), // &vArr -> ⇕ - new Transition (9247, 9248), // &varr -> ↕ - new Transition (9250, 9251), // &varrho -> ϱ - new Transition (9256, 9257), // &varsigma -> ς - new Transition (9265, 9266), // &varsubsetneq -> ⊊︀ - new Transition (9267, 9268), // &varsubsetneqq -> ⫋︀ - new Transition (9275, 9276), // &varsupsetneq -> ⊋︀ - new Transition (9277, 9278), // &varsupsetneqq -> ⫌︀ - new Transition (9283, 9284), // &vartheta -> ϑ - new Transition (9295, 9296), // &vartriangleleft -> ⊲ - new Transition (9301, 9302), // &vartriangleright -> ⊳ - new Transition (9306, 9307), // &Vbar -> ⫫ - new Transition (9310, 9311), // &vBar -> ⫨ - new Transition (9312, 9313), // &vBarv -> ⫩ - new Transition (9315, 9316), // &Vcy -> В - new Transition (9318, 9319), // &vcy -> в - new Transition (9323, 9324), // &VDash -> ⊫ - new Transition (9328, 9329), // &Vdash -> ⊩ - new Transition (9333, 9334), // &vDash -> ⊨ - new Transition (9338, 9339), // &vdash -> ⊢ - new Transition (9340, 9341), // &Vdashl -> ⫦ - new Transition (9343, 9344), // &Vee -> ⋁ - new Transition (9346, 9347), // &vee -> ∨ - new Transition (9350, 9351), // &veebar -> ⊻ - new Transition (9353, 9354), // &veeeq -> ≚ - new Transition (9358, 9359), // &vellip -> ⋮ - new Transition (9363, 9364), // &Verbar -> ‖ - new Transition (9368, 9369), // &verbar -> | - new Transition (9370, 9371), // &Vert -> ‖ - new Transition (9372, 9373), // &vert -> | - new Transition (9380, 9381), // &VerticalBar -> ∣ - new Transition (9385, 9386), // &VerticalLine -> | - new Transition (9395, 9396), // &VerticalSeparator -> ❘ - new Transition (9401, 9402), // &VerticalTilde -> ≀ - new Transition (9412, 9413), // &VeryThinSpace ->   - new Transition (9415, 9416), // &Vfr -> 𝔙 - new Transition (9418, 9419), // &vfr -> 𝔳 - new Transition (9423, 9424), // &vltri -> ⊲ - new Transition (9428, 9429), // &vnsub -> ⊂⃒ - new Transition (9430, 9431), // &vnsup -> ⊃⃒ - new Transition (9434, 9435), // &Vopf -> 𝕍 - new Transition (9438, 9439), // &vopf -> 𝕧 - new Transition (9443, 9444), // &vprop -> ∝ - new Transition (9448, 9449), // &vrtri -> ⊳ - new Transition (9452, 9453), // &Vscr -> 𝒱 - new Transition (9456, 9457), // &vscr -> 𝓋 - new Transition (9461, 9462), // &vsubnE -> ⫋︀ - new Transition (9463, 9464), // &vsubne -> ⊊︀ - new Transition (9467, 9468), // &vsupnE -> ⫌︀ - new Transition (9469, 9470), // &vsupne -> ⊋︀ - new Transition (9475, 9476), // &Vvdash -> ⊪ - new Transition (9482, 9483), // &vzigzag -> ⦚ - new Transition (9488, 9489), // &Wcirc -> Ŵ - new Transition (9494, 9495), // &wcirc -> ŵ - new Transition (9500, 9501), // &wedbar -> ⩟ - new Transition (9505, 9506), // &Wedge -> ⋀ - new Transition (9508, 9509), // &wedge -> ∧ - new Transition (9510, 9511), // &wedgeq -> ≙ - new Transition (9515, 9516), // &weierp -> ℘ - new Transition (9518, 9519), // &Wfr -> 𝔚 - new Transition (9521, 9522), // &wfr -> 𝔴 - new Transition (9525, 9526), // &Wopf -> 𝕎 - new Transition (9529, 9530), // &wopf -> 𝕨 - new Transition (9531, 9532), // &wp -> ℘ - new Transition (9533, 9534), // &wr -> ≀ - new Transition (9538, 9539), // &wreath -> ≀ - new Transition (9542, 9543), // &Wscr -> 𝒲 - new Transition (9546, 9547), // &wscr -> 𝓌 - new Transition (9551, 9552), // &xcap -> ⋂ - new Transition (9555, 9556), // &xcirc -> ◯ - new Transition (9558, 9559), // &xcup -> ⋃ - new Transition (9563, 9564), // &xdtri -> ▽ - new Transition (9567, 9568), // &Xfr -> 𝔛 - new Transition (9570, 9571), // &xfr -> 𝔵 - new Transition (9575, 9576), // &xhArr -> ⟺ - new Transition (9579, 9580), // &xharr -> ⟷ - new Transition (9581, 9582), // &Xi -> Ξ - new Transition (9583, 9584), // &xi -> ξ - new Transition (9588, 9589), // &xlArr -> ⟸ - new Transition (9592, 9593), // &xlarr -> ⟵ - new Transition (9596, 9597), // &xmap -> ⟼ - new Transition (9600, 9601), // &xnis -> ⋻ - new Transition (9605, 9606), // &xodot -> ⨀ - new Transition (9609, 9610), // &Xopf -> 𝕏 - new Transition (9612, 9613), // &xopf -> 𝕩 - new Transition (9616, 9617), // &xoplus -> ⨁ - new Transition (9621, 9622), // &xotime -> ⨂ - new Transition (9626, 9627), // &xrArr -> ⟹ - new Transition (9630, 9631), // &xrarr -> ⟶ - new Transition (9634, 9635), // &Xscr -> 𝒳 - new Transition (9638, 9639), // &xscr -> 𝓍 - new Transition (9643, 9644), // &xsqcup -> ⨆ - new Transition (9649, 9650), // &xuplus -> ⨄ - new Transition (9653, 9654), // &xutri -> △ - new Transition (9657, 9658), // &xvee -> ⋁ - new Transition (9663, 9664), // &xwedge -> ⋀ - new Transition (9670, 9671), // Ý -> Ý - new Transition (9677, 9678), // ý -> ý - new Transition (9681, 9682), // &YAcy -> Я - new Transition (9683, 9684), // &yacy -> я - new Transition (9688, 9689), // &Ycirc -> Ŷ - new Transition (9693, 9694), // &ycirc -> ŷ - new Transition (9695, 9696), // &Ycy -> Ы - new Transition (9697, 9698), // &ycy -> ы - new Transition (9700, 9701), // ¥ -> ¥ - new Transition (9703, 9704), // &Yfr -> 𝔜 - new Transition (9706, 9707), // &yfr -> 𝔶 - new Transition (9710, 9711), // &YIcy -> Ї - new Transition (9714, 9715), // &yicy -> ї - new Transition (9718, 9719), // &Yopf -> 𝕐 - new Transition (9722, 9723), // &yopf -> 𝕪 - new Transition (9726, 9727), // &Yscr -> 𝒴 - new Transition (9730, 9731), // &yscr -> 𝓎 - new Transition (9734, 9735), // &YUcy -> Ю - new Transition (9738, 9739), // &yucy -> ю - new Transition (9742, 9743), // &Yuml -> Ÿ - new Transition (9745, 9746), // ÿ -> ÿ - new Transition (9752, 9753), // &Zacute -> Ź - new Transition (9759, 9760), // &zacute -> ź - new Transition (9765, 9766), // &Zcaron -> Ž - new Transition (9771, 9772), // &zcaron -> ž - new Transition (9773, 9774), // &Zcy -> З - new Transition (9775, 9776), // &zcy -> з - new Transition (9779, 9780), // &Zdot -> Ż - new Transition (9783, 9784), // &zdot -> ż - new Transition (9789, 9790), // &zeetrf -> ℨ - new Transition (9803, 9804), // &ZeroWidthSpace -> ​ - new Transition (9806, 9807), // &Zeta -> Ζ - new Transition (9809, 9810), // &zeta -> ζ - new Transition (9812, 9813), // &Zfr -> ℨ - new Transition (9815, 9816), // &zfr -> 𝔷 - new Transition (9819, 9820), // &ZHcy -> Ж - new Transition (9823, 9824), // &zhcy -> ж - new Transition (9830, 9831), // &zigrarr -> ⇝ - new Transition (9834, 9835), // &Zopf -> ℤ - new Transition (9838, 9839), // &zopf -> 𝕫 - new Transition (9842, 9843), // &Zscr -> 𝒵 - new Transition (9846, 9847), // &zscr -> 𝓏 - new Transition (9849, 9850), // &zwj -> ‍ - new Transition (9852, 9853) // &zwnj -> ‌ - }; - TransitionTable_A = new Transition[59] { - new Transition (0, 1), // & -> &A - new Transition (1432, 1447), // &d -> &dA - new Transition (1566, 1567), // &Diacritical -> &DiacriticalA - new Transition (1580, 1581), // &DiacriticalDouble -> &DiacriticalDoubleA - new Transition (1769, 1770), // &DoubleDown -> &DoubleDownA - new Transition (1779, 1780), // &DoubleLeft -> &DoubleLeftA - new Transition (1790, 1791), // &DoubleLeftRight -> &DoubleLeftRightA - new Transition (1807, 1808), // &DoubleLongLeft -> &DoubleLongLeftA - new Transition (1818, 1819), // &DoubleLongLeftRight -> &DoubleLongLeftRightA - new Transition (1829, 1830), // &DoubleLongRight -> &DoubleLongRightA - new Transition (1840, 1841), // &DoubleRight -> &DoubleRightA - new Transition (1852, 1853), // &DoubleUp -> &DoubleUpA - new Transition (1862, 1863), // &DoubleUpDown -> &DoubleUpDownA - new Transition (1882, 1883), // &Down -> &DownA - new Transition (1908, 1909), // &DownArrowUp -> &DownArrowUpA - new Transition (2015, 2017), // &DownTee -> &DownTeeA - new Transition (2616, 2617), // &For -> &ForA - new Transition (3014, 3035), // &H -> &HA - new Transition (3020, 3046), // &h -> &hA - new Transition (3692, 3693), // &l -> &lA - new Transition (3900, 3901), // &Left -> &LeftA - new Transition (3941, 3942), // &LeftArrowRight -> &LeftArrowRightA - new Transition (4034, 4035), // &LeftRight -> &LeftRightA - new Transition (4094, 4096), // &LeftTee -> &LeftTeeA - new Transition (4440, 4441), // &LongLeft -> &LongLeftA - new Transition (4473, 4474), // &LongLeftRight -> &LongLeftRightA - new Transition (4513, 4514), // &LongRight -> &LongRightA - new Transition (4594, 4595), // &LowerLeft -> &LowerLeftA - new Transition (4605, 4606), // &LowerRight -> &LowerRightA - new Transition (5064, 5071), // &ne -> &neA - new Transition (5227, 5228), // &nh -> &nhA - new Transition (5256, 5257), // &nl -> &nlA - new Transition (5855, 5856), // &nr -> &nrA - new Transition (6084, 6085), // &nvl -> &nvlA - new Transition (6097, 6098), // &nvr -> &nvrA - new Transition (6111, 6117), // &nw -> &nwA - new Transition (6876, 6877), // &r -> &rA - new Transition (7174, 7175), // &Right -> &RightA - new Transition (7216, 7217), // &RightArrowLeft -> &RightArrowLeftA - new Transition (7339, 7341), // &RightTee -> &RightTeeA - new Transition (7703, 7709), // &se -> &seA - new Transition (7779, 7780), // &ShortDown -> &ShortDownA - new Transition (7789, 7790), // &ShortLeft -> &ShortLeftA - new Transition (7816, 7817), // &ShortRight -> &ShortRightA - new Transition (7824, 7825), // &ShortUp -> &ShortUpA - new Transition (8375, 8381), // &sw -> &swA - new Transition (8623, 8624), // &TR -> &TRA - new Transition (8775, 8785), // &u -> &uA - new Transition (8970, 8971), // &Up -> &UpA - new Transition (8997, 8998), // &UpArrowDown -> &UpArrowDownA - new Transition (9007, 9008), // &UpDown -> &UpDownA - new Transition (9074, 9075), // &UpperLeft -> &UpperLeftA - new Transition (9085, 9086), // &UpperRight -> &UpperRightA - new Transition (9110, 9112), // &UpTee -> &UpTeeA - new Transition (9201, 9243), // &v -> &vA - new Transition (9572, 9573), // &xh -> &xhA - new Transition (9585, 9586), // &xl -> &xlA - new Transition (9623, 9624), // &xr -> &xrA - new Transition (9665, 9679) // &Y -> &YA - }; - TransitionTable_B = new Transition[34] { - new Transition (0, 331), // & -> &B - new Transition (1876, 1877), // &DoubleVertical -> &DoubleVerticalB - new Transition (1882, 1915), // &Down -> &DownB - new Transition (1887, 1903), // &DownArrow -> &DownArrowB - new Transition (1981, 1983), // &DownLeftVector -> &DownLeftVectorB - new Transition (2007, 2009), // &DownRightVector -> &DownRightVectorB - new Transition (3692, 3807), // &l -> &lB - new Transition (3905, 3906), // &LeftAngle -> &LeftAngleB - new Transition (3917, 3933), // &LeftArrow -> &LeftArrowB - new Transition (3966, 3967), // &LeftDouble -> &LeftDoubleB - new Transition (3992, 3994), // &LeftDownVector -> &LeftDownVectorB - new Transition (4126, 4128), // &LeftTriangle -> &LeftTriangleB - new Transition (4166, 4168), // &LeftUpVector -> &LeftUpVectorB - new Transition (4177, 4179), // &LeftVector -> &LeftVectorB - new Transition (5347, 5348), // &No -> &NoB - new Transition (5354, 5355), // &Non -> &NonB - new Transition (5409, 5410), // &NotDoubleVertical -> &NotDoubleVerticalB - new Transition (5539, 5541), // &NotLeftTriangle -> &NotLeftTriangleB - new Transition (5682, 5684), // &NotRightTriangle -> &NotRightTriangleB - new Transition (5816, 5817), // &NotVertical -> &NotVerticalB - new Transition (6437, 6438), // &Over -> &OverB - new Transition (6876, 6991), // &r -> &rB - new Transition (6886, 6986), // &R -> &RB - new Transition (7179, 7180), // &RightAngle -> &RightAngleB - new Transition (7191, 7209), // &RightArrow -> &RightArrowB - new Transition (7241, 7242), // &RightDouble -> &RightDoubleB - new Transition (7267, 7269), // &RightDownVector -> &RightDownVectorB - new Transition (7371, 7373), // &RightTriangle -> &RightTriangleB - new Transition (7411, 7413), // &RightUpVector -> &RightUpVectorB - new Transition (7422, 7424), // &RightVector -> &RightVectorB - new Transition (8919, 8920), // &Under -> &UnderB - new Transition (8975, 8990), // &UpArrow -> &UpArrowB - new Transition (9201, 9308), // &v -> &vB - new Transition (9377, 9378) // &Vertical -> &VerticalB - }; - TransitionTable_C = new Transition[15] { - new Transition (0, 789), // & -> &C - new Transition (1075, 1076), // &Clockwise -> &ClockwiseC - new Transition (1093, 1094), // &Close -> &CloseC - new Transition (1230, 1231), // &Counter -> &CounterC - new Transition (1239, 1240), // &CounterClockwise -> &CounterClockwiseC - new Transition (1316, 1326), // &Cup -> &CupC - new Transition (1747, 1748), // &Double -> &DoubleC - new Transition (3450, 3451), // &Invisible -> &InvisibleC - new Transition (3900, 3953), // &Left -> &LeftC - new Transition (5376, 5380), // &Not -> &NotC - new Transition (5391, 5392), // &NotCup -> &NotCupC - new Transition (6308, 6309), // &Open -> &OpenC - new Transition (7174, 7228), // &Right -> &RightC - new Transition (7756, 7757), // &SH -> &SHC - new Transition (7886, 7887) // &Small -> &SmallC - }; - TransitionTable_D = new Transition[43] { - new Transition (0, 1425), // & -> &D - new Transition (613, 618), // &box -> &boxD - new Transition (636, 640), // &boxH -> &boxHD - new Transition (638, 644), // &boxh -> &boxhD - new Transition (831, 832), // &Capital -> &CapitalD - new Transition (843, 844), // &CapitalDifferential -> &CapitalDifferentialD - new Transition (939, 940), // &Center -> &CenterD - new Transition (1023, 1024), // &Circle -> &CircleD - new Transition (1098, 1099), // &CloseCurly -> &CloseCurlyD - new Transition (1425, 1490), // &D -> &DD - new Transition (1566, 1573), // &Diacritical -> &DiacriticalD - new Transition (1630, 1631), // &Differential -> &DifferentialD - new Transition (1692, 1696), // &Dot -> &DotD - new Transition (1747, 1764), // &Double -> &DoubleD - new Transition (1852, 1859), // &DoubleUp -> &DoubleUpD - new Transition (2115, 2157), // &e -> &eD - new Transition (2157, 2158), // &eD -> &eDD - new Transition (2175, 2176), // &ef -> &efD - new Transition (2397, 2399), // &equiv -> &equivD - new Transition (2399, 2400), // &equivD -> &equivDD - new Transition (2409, 2414), // &er -> &erD - new Transition (3036, 3037), // &HAR -> &HARD - new Transition (3209, 3210), // &Hump -> &HumpD - new Transition (3900, 3961), // &Left -> &LeftD - new Transition (4139, 4140), // &LeftUp -> &LeftUpD - new Transition (4767, 4825), // &m -> &mD - new Transition (4825, 4826), // &mD -> &mDD - new Transition (5376, 5396), // &Not -> &NotD - new Transition (5496, 5497), // &NotHump -> &NotHumpD - new Transition (6043, 6058), // &nv -> &nvD - new Transition (6047, 6048), // &nV -> &nVD - new Transition (6313, 6314), // &OpenCurly -> &OpenCurlyD - new Transition (6488, 6489), // &Partial -> &PartialD - new Transition (7174, 7236), // &Right -> &RightD - new Transition (7384, 7385), // &RightUp -> &RightUpD - new Transition (7592, 7593), // &Rule -> &RuleD - new Transition (7775, 7776), // &Short -> &ShortD - new Transition (8624, 8625), // &TRA -> &TRAD - new Transition (8680, 8681), // &Triple -> &TripleD - new Transition (8970, 9004), // &Up -> &UpD - new Transition (8975, 8994), // &UpArrow -> &UpArrowD - new Transition (9201, 9330), // &v -> &vD - new Transition (9303, 9320) // &V -> &VD - }; - TransitionTable_E = new Transition[81] { - new Transition (0, 2108), // & -> &E - new Transition (1, 50), // &A -> &AE - new Transition (27, 31), // &ac -> &acE - new Transition (199, 206), // &ap -> &apE - new Transition (775, 777), // &bump -> &bumpE - new Transition (979, 1049), // &cir -> &cirE - new Transition (1692, 1707), // &Dot -> &DotE - new Transition (2490, 2491), // &Exponential -> &ExponentialE - new Transition (2701, 2763), // &g -> &gE - new Transition (2824, 2828), // &gl -> &glE - new Transition (2832, 2841), // &gn -> &gnE - new Transition (2871, 2872), // &Greater -> &GreaterE - new Transition (2886, 2887), // &GreaterFull -> &GreaterFullE - new Transition (2910, 2911), // &GreaterSlant -> &GreaterSlantE - new Transition (3011, 3012), // &gvn -> &gvnE - new Transition (3209, 3219), // &Hump -> &HumpE - new Transition (3236, 3269), // &I -> &IE - new Transition (3512, 3518), // &isin -> &isinE - new Transition (3692, 3894), // &l -> &lE - new Transition (4126, 4132), // &LeftTriangle -> &LeftTriangleE - new Transition (4239, 4240), // &Less -> &LessE - new Transition (4256, 4257), // &LessFull -> &LessFullE - new Transition (4288, 4289), // &LessSlant -> &LessSlantE - new Transition (4317, 4319), // &lg -> &lgE - new Transition (4401, 4410), // &ln -> &lnE - new Transition (4764, 4765), // &lvn -> &lvnE - new Transition (4986, 4988), // &nap -> &napE - new Transition (5195, 5196), // &ng -> &ngE - new Transition (5256, 5268), // &nl -> &nlE - new Transition (5376, 5414), // &Not -> &NotE - new Transition (5445, 5447), // &NotGreater -> &NotGreaterE - new Transition (5456, 5457), // &NotGreaterFull -> &NotGreaterFullE - new Transition (5480, 5481), // &NotGreaterSlant -> &NotGreaterSlantE - new Transition (5496, 5506), // &NotHump -> &NotHumpE - new Transition (5513, 5519), // ¬in -> ¬inE - new Transition (5539, 5545), // &NotLeftTriangle -> &NotLeftTriangleE - new Transition (5552, 5554), // &NotLess -> &NotLessE - new Transition (5577, 5578), // &NotLessSlant -> &NotLessSlantE - new Transition (5637, 5639), // &NotPrecedes -> &NotPrecedesE - new Transition (5649, 5650), // &NotPrecedesSlant -> &NotPrecedesSlantE - new Transition (5662, 5663), // &NotReverse -> &NotReverseE - new Transition (5682, 5688), // &NotRightTriangle -> &NotRightTriangleE - new Transition (5705, 5707), // &NotSquareSubset -> &NotSquareSubsetE - new Transition (5718, 5720), // &NotSquareSuperset -> &NotSquareSupersetE - new Transition (5730, 5732), // &NotSubset -> &NotSubsetE - new Transition (5743, 5745), // &NotSucceeds -> &NotSucceedsE - new Transition (5755, 5756), // &NotSucceedsSlant -> &NotSucceedsSlantE - new Transition (5773, 5775), // &NotSuperset -> &NotSupersetE - new Transition (5785, 5787), // &NotTilde -> &NotTildeE - new Transition (5796, 5797), // &NotTildeFull -> &NotTildeFullE - new Transition (5952, 5954), // &nsub -> &nsubE - new Transition (5973, 5975), // &nsup -> &nsupE - new Transition (6131, 6190), // &O -> &OE - new Transition (6642, 6651), // &pr -> &prE - new Transition (6677, 6679), // &Precedes -> &PrecedesE - new Transition (6689, 6690), // &PrecedesSlant -> &PrecedesSlantE - new Transition (6735, 6739), // &prn -> &prnE - new Transition (6886, 7092), // &R -> &RE - new Transition (7101, 7102), // &Reverse -> &ReverseE - new Transition (7122, 7123), // &ReverseUp -> &ReverseUpE - new Transition (7371, 7377), // &RightTriangle -> &RightTriangleE - new Transition (7631, 7649), // &sc -> &scE - new Transition (7670, 7674), // &scn -> &scnE - new Transition (7857, 7859), // &simg -> &simgE - new Transition (7861, 7863), // &siml -> &simlE - new Transition (8037, 8039), // &SquareSubset -> &SquareSubsetE - new Transition (8050, 8052), // &SquareSuperset -> &SquareSupersetE - new Transition (8131, 8137), // &sub -> &subE - new Transition (8150, 8151), // &subn -> &subnE - new Transition (8167, 8178), // &Subset -> &SubsetE - new Transition (8221, 8223), // &Succeeds -> &SucceedsE - new Transition (8233, 8234), // &SucceedsSlant -> &SucceedsSlantE - new Transition (8284, 8300), // &sup -> &supE - new Transition (8312, 8314), // &Superset -> &SupersetE - new Transition (8338, 8339), // &supn -> &supnE - new Transition (8547, 8554), // &Tilde -> &TildeE - new Transition (8563, 8564), // &TildeFull -> &TildeFullE - new Transition (8625, 8626), // &TRAD -> &TRADE - new Transition (8970, 9034), // &Up -> &UpE - new Transition (9460, 9461), // &vsubn -> &vsubnE - new Transition (9466, 9467) // &vsupn -> &vsupnE - }; - TransitionTable_F = new Transition[10] { - new Transition (0, 2517), // & -> &F - new Transition (219, 220), // &Apply -> &ApplyF - new Transition (2871, 2883), // &Greater -> &GreaterF - new Transition (3900, 3998), // &Left -> &LeftF - new Transition (4239, 4253), // &Less -> &LessF - new Transition (5445, 5453), // &NotGreater -> &NotGreaterF - new Transition (5785, 5793), // &NotTilde -> &NotTildeF - new Transition (7174, 7273), // &Right -> &RightF - new Transition (7930, 7931), // &SO -> &SOF - new Transition (8547, 8560) // &Tilde -> &TildeF - }; - TransitionTable_G = new Transition[15] { - new Transition (0, 2708), // & -> &G - new Transition (1566, 1587), // &Diacritical -> &DiacriticalG - new Transition (2287, 2288), // &EN -> &ENG - new Transition (2871, 2893), // &Greater -> &GreaterG - new Transition (4239, 4263), // &Less -> &LessG - new Transition (4244, 4245), // &LessEqual -> &LessEqualG - new Transition (4965, 5212), // &n -> &nG - new Transition (5151, 5152), // &Nested -> &NestedG - new Transition (5158, 5159), // &NestedGreater -> &NestedGreaterG - new Transition (5376, 5439), // &Not -> &NotG - new Transition (5445, 5463), // &NotGreater -> &NotGreaterG - new Transition (5552, 5560), // &NotLess -> &NotLessG - new Transition (5595, 5596), // &NotNested -> &NotNestedG - new Transition (5602, 5603), // &NotNestedGreater -> &NotNestedGreaterG - new Transition (7092, 7093) // &RE -> ® - }; - TransitionTable_H = new Transition[20] { - new Transition (0, 3014), // & -> &H - new Transition (613, 636), // &box -> &boxH - new Transition (691, 695), // &boxV -> &boxVH - new Transition (693, 699), // &boxv -> &boxvH - new Transition (789, 956), // &C -> &CH - new Transition (1432, 1546), // &d -> &dH - new Transition (2442, 2443), // &ET -> Ð - new Transition (3213, 3214), // &HumpDown -> &HumpDownH - new Transition (3618, 3660), // &K -> &KH - new Transition (3692, 4321), // &l -> &lH - new Transition (5376, 5493), // &Not -> &NotH - new Transition (5500, 5501), // &NotHumpDown -> &NotHumpDownH - new Transition (6043, 6073), // &nv -> &nvH - new Transition (6876, 7151), // &r -> &rH - new Transition (7610, 7756), // &S -> &SH - new Transition (7757, 7758), // &SHC -> &SHCH - new Transition (8400, 8535), // &T -> &TH - new Transition (8713, 8719), // &TS -> &TSH - new Transition (8775, 8872), // &u -> &uH - new Transition (9747, 9817) // &Z -> &ZH - }; - TransitionTable_I = new Transition[9] { - new Transition (0, 3236), // & -> &I - new Transition (1082, 1083), // &ClockwiseContour -> &ClockwiseContourI - new Transition (1190, 1191), // &Contour -> &ContourI - new Transition (1246, 1247), // &CounterClockwiseContour -> &CounterClockwiseContourI - new Transition (1754, 1755), // &DoubleContour -> &DoubleContourI - new Transition (3349, 3350), // &Imaginary -> &ImaginaryI - new Transition (7503, 7504), // &Round -> &RoundI - new Transition (8013, 8019), // &Square -> &SquareI - new Transition (9665, 9708) // &Y -> &YI - }; - TransitionTable_J = new Transition[7] { - new Transition (0, 3555), // & -> &J - new Transition (1425, 1661), // &D -> &DJ - new Transition (2708, 2816), // &G -> &GJ - new Transition (3236, 3320), // &I -> &IJ - new Transition (3618, 3668), // &K -> &KJ - new Transition (3698, 4338), // &L -> &LJ - new Transition (4971, 5248) // &N -> &NJ - }; - TransitionTable_K = new Transition[1] { - new Transition (0, 3618) // & -> &K - }; - TransitionTable_L = new Transition[29] { - new Transition (0, 3698), // & -> &L - new Transition (618, 619), // &boxD -> &boxDL - new Transition (623, 624), // &boxd -> &boxdL - new Transition (673, 674), // &boxU -> &boxUL - new Transition (678, 679), // &boxu -> &boxuL - new Transition (691, 703), // &boxV -> &boxVL - new Transition (693, 707), // &boxv -> &boxvL - new Transition (1747, 1776), // &Double -> &DoubleL - new Transition (1803, 1804), // &DoubleLong -> &DoubleLongL - new Transition (1882, 1950), // &Down -> &DownL - new Transition (2871, 2901), // &Greater -> &GreaterL - new Transition (2876, 2878), // &GreaterEqual -> &GreaterEqualL - new Transition (3178, 3179), // &Horizontal -> &HorizontalL - new Transition (4239, 4275), // &Less -> &LessL - new Transition (4436, 4437), // &Long -> &LongL - new Transition (4590, 4591), // &Lower -> &LowerL - new Transition (4965, 5272), // &n -> &nL - new Transition (5151, 5167), // &Nested -> &NestedL - new Transition (5170, 5171), // &NestedLess -> &NestedLessL - new Transition (5176, 5177), // &New -> &NewL - new Transition (5376, 5528), // &Not -> &NotL - new Transition (5445, 5471), // &NotGreater -> &NotGreaterL - new Transition (5552, 5568), // &NotLess -> &NotLessL - new Transition (5595, 5611), // &NotNested -> &NotNestedL - new Transition (5614, 5615), // &NotNestedLess -> &NotNestedLessL - new Transition (7191, 7213), // &RightArrow -> &RightArrowL - new Transition (7775, 7786), // &Short -> &ShortL - new Transition (9070, 9071), // &Upper -> &UpperL - new Transition (9377, 9382) // &Vertical -> &VerticalL - }; - TransitionTable_M = new Transition[5] { - new Transition (0, 4781), // & -> &M - new Transition (1, 111), // &A -> &AM - new Transition (1023, 1032), // &Circle -> &CircleM - new Transition (5090, 5091), // &Negative -> &NegativeM - new Transition (6589, 6590) // &Plus -> &PlusM - }; - TransitionTable_N = new Transition[5] { - new Transition (0, 4971), // & -> &N - new Transition (301, 587), // &b -> &bN - new Transition (2108, 2287), // &E -> &EN - new Transition (5376, 5590), // &Not -> &NotN - new Transition (8537, 8538) // &THOR -> Þ - }; - TransitionTable_O = new Transition[6] { - new Transition (0, 6131), // & -> &O - new Transition (789, 1217), // &C -> &CO - new Transition (3236, 3463), // &I -> &IO - new Transition (6869, 6870), // &QU -> &QUO - new Transition (7610, 7930), // &S -> &SO - new Transition (8535, 8536) // &TH -> &THO - }; - TransitionTable_P = new Transition[11] { - new Transition (0, 6482), // & -> &P - new Transition (111, 112), // &AM -> & - new Transition (1023, 1038), // &Circle -> &CircleP - new Transition (1217, 1218), // &CO -> &COP - new Transition (2954, 2955), // >l -> >lP - new Transition (4731, 4738), // <r -> <rP - new Transition (4903, 4904), // &Minus -> &MinusP - new Transition (5376, 5630), // &Not -> &NotP - new Transition (6437, 6451), // &Over -> &OverP - new Transition (8919, 8933), // &Under -> &UnderP - new Transition (8947, 8949) // &Union -> &UnionP - }; - TransitionTable_Q = new Transition[5] { - new Transition (0, 6813), // & -> &Q - new Transition (1098, 1111), // &CloseCurly -> &CloseCurlyQ - new Transition (1104, 1105), // &CloseCurlyDouble -> &CloseCurlyDoubleQ - new Transition (6313, 6326), // &OpenCurly -> &OpenCurlyQ - new Transition (6319, 6320) // &OpenCurlyDouble -> &OpenCurlyDoubleQ - }; - TransitionTable_R = new Transition[26] { - new Transition (0, 6886), // & -> &R - new Transition (618, 628), // &boxD -> &boxDR - new Transition (623, 632), // &boxd -> &boxdR - new Transition (673, 683), // &boxU -> &boxUR - new Transition (678, 687), // &boxu -> &boxuR - new Transition (691, 711), // &boxV -> &boxVR - new Transition (693, 715), // &boxv -> &boxvR - new Transition (1004, 1028), // &circled -> &circledR - new Transition (1747, 1836), // &Double -> &DoubleR - new Transition (1779, 1786), // &DoubleLeft -> &DoubleLeftR - new Transition (1803, 1825), // &DoubleLong -> &DoubleLongR - new Transition (1807, 1814), // &DoubleLongLeft -> &DoubleLongLeftR - new Transition (1882, 1987), // &Down -> &DownR - new Transition (1953, 1954), // &DownLeft -> &DownLeftR - new Transition (3035, 3036), // &HA -> &HAR - new Transition (3900, 4030), // &Left -> &LeftR - new Transition (3917, 3937), // &LeftArrow -> &LeftArrowR - new Transition (4436, 4509), // &Long -> &LongR - new Transition (4440, 4469), // &LongLeft -> &LongLeftR - new Transition (4590, 4601), // &Lower -> &LowerR - new Transition (4965, 5868), // &n -> &nR - new Transition (5376, 5656), // &Not -> &NotR - new Transition (7775, 7812), // &Short -> &ShortR - new Transition (8400, 8623), // &T -> &TR - new Transition (8536, 8537), // &THO -> &THOR - new Transition (9070, 9081) // &Upper -> &UpperR - }; - TransitionTable_S = new Transition[36] { - new Transition (0, 7610), // & -> &S - new Transition (1004, 1030), // &circled -> &circledS - new Transition (1425, 2048), // &D -> &DS - new Transition (2248, 2249), // &Empty -> &EmptyS - new Transition (2253, 2254), // &EmptySmall -> &EmptySmallS - new Transition (2266, 2267), // &EmptyVery -> &EmptyVeryS - new Transition (2271, 2272), // &EmptyVerySmall -> &EmptyVerySmallS - new Transition (2558, 2559), // &Filled -> &FilledS - new Transition (2563, 2564), // &FilledSmall -> &FilledSmallS - new Transition (2574, 2575), // &FilledVery -> &FilledVeryS - new Transition (2579, 2580), // &FilledVerySmall -> &FilledVerySmallS - new Transition (2871, 2906), // &Greater -> &GreaterS - new Transition (3105, 3106), // &Hilbert -> &HilbertS - new Transition (4239, 4284), // &Less -> &LessS - new Transition (4847, 4848), // &Medium -> &MediumS - new Transition (5096, 5097), // &NegativeMedium -> &NegativeMediumS - new Transition (5107, 5108), // &NegativeThick -> &NegativeThickS - new Transition (5114, 5115), // &NegativeThin -> &NegativeThinS - new Transition (5128, 5129), // &NegativeVeryThin -> &NegativeVeryThinS - new Transition (5362, 5363), // &NonBreaking -> &NonBreakingS - new Transition (5376, 5694), // &Not -> &NotS - new Transition (5445, 5476), // &NotGreater -> &NotGreaterS - new Transition (5552, 5573), // &NotLess -> &NotLessS - new Transition (5637, 5645), // &NotPrecedes -> &NotPrecedesS - new Transition (5699, 5700), // &NotSquare -> &NotSquareS - new Transition (5743, 5751), // &NotSucceeds -> &NotSucceedsS - new Transition (6138, 6376), // &o -> &oS - new Transition (6677, 6685), // &Precedes -> &PrecedesS - new Transition (8013, 8032), // &Square -> &SquareS - new Transition (8221, 8229), // &Succeeds -> &SucceedsS - new Transition (8400, 8713), // &T -> &TS - new Transition (8509, 8510), // &Thick -> &ThickS - new Transition (8520, 8521), // &Thin -> &ThinS - new Transition (9377, 9387), // &Vertical -> &VerticalS - new Transition (9407, 9408), // &VeryThin -> &VeryThinS - new Transition (9798, 9799) // &ZeroWidth -> &ZeroWidthS - }; - TransitionTable_T = new Transition[40] { - new Transition (0, 8400), // & -> &T - new Transition (1023, 1043), // &Circle -> &CircleT - new Transition (1566, 1593), // &Diacritical -> &DiacriticalT - new Transition (1779, 1797), // &DoubleLeft -> &DoubleLeftT - new Transition (1840, 1847), // &DoubleRight -> &DoubleRightT - new Transition (1882, 2013), // &Down -> &DownT - new Transition (1953, 1966), // &DownLeft -> &DownLeftT - new Transition (1991, 1992), // &DownRight -> &DownRightT - new Transition (2108, 2442), // &E -> &ET - new Transition (2370, 2377), // &Equal -> &EqualT - new Transition (2708, 2938), // &G -> > - new Transition (2871, 2917), // &Greater -> &GreaterT - new Transition (3450, 3457), // &Invisible -> &InvisibleT - new Transition (3698, 4694), // &L -> < - new Transition (3900, 4092), // &Left -> &LeftT - new Transition (3976, 3977), // &LeftDown -> &LeftDownT - new Transition (4139, 4151), // &LeftUp -> &LeftUpT - new Transition (4239, 4295), // &Less -> &LessT - new Transition (5090, 5103), // &Negative -> &NegativeT - new Transition (5124, 5125), // &NegativeVery -> &NegativeVeryT - new Transition (5376, 5781), // &Not -> &NotT - new Transition (5425, 5427), // &NotEqual -> &NotEqualT - new Transition (5445, 5487), // &NotGreater -> &NotGreaterT - new Transition (5531, 5532), // &NotLeft -> &NotLeftT - new Transition (5552, 5584), // &NotLess -> &NotLessT - new Transition (5674, 5675), // &NotRight -> &NotRightT - new Transition (5743, 5762), // &NotSucceeds -> &NotSucceedsT - new Transition (5785, 5803), // &NotTilde -> &NotTildeT - new Transition (6677, 6696), // &Precedes -> &PrecedesT - new Transition (6870, 6871), // &QUO -> " - new Transition (7174, 7337), // &Right -> &RightT - new Transition (7251, 7252), // &RightDown -> &RightDownT - new Transition (7384, 7396), // &RightUp -> &RightUpT - new Transition (7931, 7932), // &SOF -> &SOFT - new Transition (8221, 8240), // &Succeeds -> &SucceedsT - new Transition (8269, 8270), // &Such -> &SuchT - new Transition (8547, 8570), // &Tilde -> &TildeT - new Transition (8970, 9108), // &Up -> &UpT - new Transition (9377, 9397), // &Vertical -> &VerticalT - new Transition (9403, 9404) // &Very -> &VeryT - }; - TransitionTable_U = new Transition[13] { - new Transition (0, 8768), // & -> &U - new Transition (613, 673), // &box -> &boxU - new Transition (636, 648), // &boxH -> &boxHU - new Transition (638, 652), // &boxh -> &boxhU - new Transition (1747, 1851), // &Double -> &DoubleU - new Transition (1887, 1907), // &DownArrow -> &DownArrowU - new Transition (3900, 4138), // &Left -> &LeftU - new Transition (6813, 6869), // &Q -> &QU - new Transition (7101, 7121), // &Reverse -> &ReverseU - new Transition (7174, 7383), // &Right -> &RightU - new Transition (7775, 7823), // &Short -> &ShortU - new Transition (8013, 8058), // &Square -> &SquareU - new Transition (9665, 9732) // &Y -> &YU - }; - TransitionTable_V = new Transition[29] { - new Transition (0, 9303), // & -> &V - new Transition (613, 691), // &box -> &boxV - new Transition (1747, 1869), // &Double -> &DoubleV - new Transition (1953, 1976), // &DownLeft -> &DownLeftV - new Transition (1958, 1959), // &DownLeftRight -> &DownLeftRightV - new Transition (1968, 1969), // &DownLeftTee -> &DownLeftTeeV - new Transition (1991, 2002), // &DownRight -> &DownRightV - new Transition (1994, 1995), // &DownRightTee -> &DownRightTeeV - new Transition (2248, 2263), // &Empty -> &EmptyV - new Transition (2558, 2571), // &Filled -> &FilledV - new Transition (3900, 4172), // &Left -> &LeftV - new Transition (3976, 3987), // &LeftDown -> &LeftDownV - new Transition (3979, 3980), // &LeftDownTee -> &LeftDownTeeV - new Transition (4034, 4085), // &LeftRight -> &LeftRightV - new Transition (4094, 4102), // &LeftTee -> &LeftTeeV - new Transition (4139, 4161), // &LeftUp -> &LeftUpV - new Transition (4143, 4144), // &LeftUpDown -> &LeftUpDownV - new Transition (4153, 4154), // &LeftUpTee -> &LeftUpTeeV - new Transition (4965, 6047), // &n -> &nV - new Transition (5090, 5121), // &Negative -> &NegativeV - new Transition (5376, 5809), // &Not -> &NotV - new Transition (5401, 5402), // &NotDouble -> &NotDoubleV - new Transition (7174, 7417), // &Right -> &RightV - new Transition (7251, 7262), // &RightDown -> &RightDownV - new Transition (7254, 7255), // &RightDownTee -> &RightDownTeeV - new Transition (7339, 7347), // &RightTee -> &RightTeeV - new Transition (7384, 7406), // &RightUp -> &RightUpV - new Transition (7388, 7389), // &RightUpDown -> &RightUpDownV - new Transition (7398, 7399) // &RightUpTee -> &RightUpTeeV - }; - TransitionTable_W = new Transition[2] { - new Transition (0, 9484), // & -> &W - new Transition (9793, 9794) // &Zero -> &ZeroW - }; - TransitionTable_X = new Transition[1] { - new Transition (0, 9565) // & -> &X - }; - TransitionTable_Y = new Transition[2] { - new Transition (0, 9665), // & -> &Y - new Transition (1218, 1219) // &COP -> © - }; - TransitionTable_Z = new Transition[2] { - new Transition (0, 9747), // & -> &Z - new Transition (1425, 2093) // &D -> &DZ - }; - TransitionTable_a = new Transition[555] { - new Transition (0, 8), // & -> &a - new Transition (1, 2), // &A -> &Aa - new Transition (8, 9), // &a -> &aa - new Transition (68, 69), // &Agr -> &Agra - new Transition (74, 75), // &agr -> &agra - new Transition (91, 92), // &Alph -> &Alpha - new Transition (95, 96), // &alph -> &alpha - new Transition (98, 99), // &Am -> &Ama - new Transition (103, 104), // &am -> &ama - new Transition (120, 122), // &and -> &anda - new Transition (145, 147), // &angmsd -> &angmsda - new Transition (147, 148), // &angmsda -> &angmsdaa - new Transition (178, 179), // &angz -> &angza - new Transition (199, 201), // &ap -> &apa - new Transition (301, 302), // &b -> &ba - new Transition (331, 332), // &B -> &Ba - new Transition (336, 337), // &Backsl -> &Backsla - new Transition (385, 386), // &bec -> &beca - new Transition (391, 392), // &Bec -> &Beca - new Transition (423, 424), // &Bet -> &Beta - new Transition (426, 427), // &bet -> &beta - new Transition (444, 445), // &bigc -> &bigca - new Transition (477, 478), // &bigst -> &bigsta - new Transition (483, 484), // &bigtri -> &bigtria - new Transition (513, 514), // &bk -> &bka - new Transition (519, 520), // &bl -> &bla - new Transition (533, 534), // &blacksqu -> &blacksqua - new Transition (540, 541), // &blacktri -> &blacktria - new Transition (736, 737), // &brvb -> &brvba - new Transition (789, 790), // &C -> &Ca - new Transition (796, 797), // &c -> &ca - new Transition (805, 807), // &cap -> &capa - new Transition (817, 818), // &capc -> &capca - new Transition (829, 830), // &Capit -> &Capita - new Transition (841, 842), // &CapitalDifferenti -> &CapitalDifferentia - new Transition (861, 862), // &cc -> &cca - new Transition (866, 867), // &Cc -> &Cca - new Transition (924, 925), // &Cedill -> &Cedilla - new Transition (968, 969), // &checkm -> &checkma - new Transition (987, 988), // &circle -> &circlea - new Transition (1004, 1005), // &circled -> &circleda - new Transition (1014, 1015), // &circledd -> &circledda - new Transition (1088, 1089), // &ClockwiseContourIntegr -> &ClockwiseContourIntegra - new Transition (1143, 1144), // &comm -> &comma - new Transition (1196, 1197), // &ContourIntegr -> &ContourIntegra - new Transition (1252, 1253), // &CounterClockwiseContourIntegr -> &CounterClockwiseContourIntegra - new Transition (1256, 1257), // &cr -> &cra - new Transition (1293, 1294), // &cud -> &cuda - new Transition (1308, 1309), // &cul -> &cula - new Transition (1322, 1323), // &cupbrc -> &cupbrca - new Transition (1326, 1327), // &CupC -> &CupCa - new Transition (1330, 1331), // &cupc -> &cupca - new Transition (1346, 1347), // &cur -> &cura - new Transition (1382, 1383), // &curve -> &curvea - new Transition (1425, 1426), // &D -> &Da - new Transition (1432, 1433), // &d -> &da - new Transition (1464, 1465), // &dbk -> &dbka - new Transition (1470, 1471), // &dbl -> &dbla - new Transition (1474, 1475), // &Dc -> &Dca - new Transition (1480, 1481), // &dc -> &dca - new Transition (1492, 1494), // &dd -> &dda - new Transition (1505, 1506), // &DDotr -> &DDotra - new Transition (1522, 1523), // &Delt -> &Delta - new Transition (1526, 1527), // &delt -> &delta - new Transition (1546, 1547), // &dH -> &dHa - new Transition (1550, 1551), // &dh -> &dha - new Transition (1557, 1558), // &Di -> &Dia - new Transition (1564, 1565), // &Diacritic -> &Diacritica - new Transition (1588, 1589), // &DiacriticalGr -> &DiacriticalGra - new Transition (1599, 1600), // &di -> &dia - new Transition (1628, 1629), // &Differenti -> &Differentia - new Transition (1633, 1634), // &dig -> &diga - new Transition (1636, 1637), // &digamm -> &digamma - new Transition (1681, 1682), // &doll -> &dolla - new Transition (1709, 1710), // &DotEqu -> &DotEqua - new Transition (1726, 1727), // &dotsqu -> &dotsqua - new Transition (1735, 1736), // &doubleb -> &doubleba - new Transition (1760, 1761), // &DoubleContourIntegr -> &DoubleContourIntegra - new Transition (1874, 1875), // &DoubleVertic -> &DoubleVertica - new Transition (1877, 1878), // &DoubleVerticalB -> &DoubleVerticalBa - new Transition (1882, 1889), // &Down -> &Downa - new Transition (1896, 1897), // &down -> &downa - new Transition (1903, 1904), // &DownArrowB -> &DownArrowBa - new Transition (1924, 1925), // &downdown -> &downdowna - new Transition (1932, 1933), // &downh -> &downha - new Transition (1983, 1984), // &DownLeftVectorB -> &DownLeftVectorBa - new Transition (2009, 2010), // &DownRightVectorB -> &DownRightVectorBa - new Transition (2025, 2026), // &drbk -> &drbka - new Transition (2077, 2078), // &du -> &dua - new Transition (2082, 2083), // &duh -> &duha - new Transition (2086, 2087), // &dw -> &dwa - new Transition (2103, 2104), // &dzigr -> &dzigra - new Transition (2108, 2109), // &E -> &Ea - new Transition (2115, 2116), // &e -> &ea - new Transition (2127, 2128), // &Ec -> &Eca - new Transition (2133, 2134), // &ec -> &eca - new Transition (2188, 2189), // &Egr -> &Egra - new Transition (2193, 2194), // &egr -> &egra - new Transition (2228, 2229), // &Em -> &Ema - new Transition (2233, 2234), // &em -> &ema - new Transition (2250, 2251), // &EmptySm -> &EmptySma - new Transition (2256, 2257), // &EmptySmallSqu -> &EmptySmallSqua - new Transition (2268, 2269), // &EmptyVerySm -> &EmptyVerySma - new Transition (2274, 2275), // &EmptyVerySmallSqu -> &EmptyVerySmallSqua - new Transition (2312, 2313), // &ep -> &epa - new Transition (2354, 2355), // &eqsl -> &eqsla - new Transition (2368, 2369), // &Equ -> &Equa - new Transition (2372, 2373), // &equ -> &equa - new Transition (2403, 2404), // &eqvp -> &eqvpa - new Transition (2409, 2410), // &er -> &era - new Transition (2436, 2437), // &Et -> &Eta - new Transition (2439, 2440), // &et -> &eta - new Transition (2475, 2476), // &expect -> &expecta - new Transition (2488, 2489), // &Exponenti -> &Exponentia - new Transition (2498, 2499), // &exponenti -> &exponentia - new Transition (2503, 2504), // &f -> &fa - new Transition (2525, 2526), // &fem -> &fema - new Transition (2560, 2561), // &FilledSm -> &FilledSma - new Transition (2566, 2567), // &FilledSmallSqu -> &FilledSmallSqua - new Transition (2576, 2577), // &FilledVerySm -> &FilledVerySma - new Transition (2582, 2583), // &FilledVerySmallSqu -> &FilledVerySmallSqua - new Transition (2592, 2593), // &fl -> &fla - new Transition (2621, 2622), // &for -> &fora - new Transition (2639, 2640), // &fp -> &fpa - new Transition (2647, 2648), // &fr -> &fra - new Transition (2701, 2702), // &g -> &ga - new Transition (2708, 2709), // &G -> &Ga - new Transition (2711, 2712), // &Gamm -> &Gamma - new Transition (2715, 2716), // &gamm -> &gamma - new Transition (2776, 2777), // &geqsl -> &geqsla - new Transition (2824, 2826), // &gl -> &gla - new Transition (2832, 2833), // &gn -> &gna - new Transition (2861, 2862), // &gr -> &gra - new Transition (2867, 2868), // &Gre -> &Grea - new Transition (2874, 2875), // &GreaterEqu -> &GreaterEqua - new Transition (2889, 2890), // &GreaterFullEqu -> &GreaterFullEqua - new Transition (2895, 2896), // &GreaterGre -> &GreaterGrea - new Transition (2907, 2908), // &GreaterSl -> &GreaterSla - new Transition (2913, 2914), // &GreaterSlantEqu -> &GreaterSlantEqua - new Transition (2955, 2956), // >lP -> >lPa - new Transition (2965, 2966), // >r -> >ra - new Transition (3014, 3015), // &H -> &Ha - new Transition (3020, 3021), // &h -> &ha - new Transition (3060, 3061), // &hb -> &hba - new Transition (3074, 3075), // &he -> &hea - new Transition (3107, 3108), // &HilbertSp -> &HilbertSpa - new Transition (3114, 3115), // &hkse -> &hksea - new Transition (3120, 3121), // &hksw -> &hkswa - new Transition (3126, 3127), // &ho -> &hoa - new Transition (3141, 3142), // &hookleft -> &hooklefta - new Transition (3152, 3153), // &hookright -> &hookrighta - new Transition (3167, 3168), // &horb -> &horba - new Transition (3176, 3177), // &Horizont -> &Horizonta - new Transition (3192, 3193), // &hsl -> &hsla - new Transition (3221, 3222), // &HumpEqu -> &HumpEqua - new Transition (3236, 3237), // &I -> &Ia - new Transition (3243, 3244), // &i -> &ia - new Transition (3290, 3291), // &Igr -> &Igra - new Transition (3296, 3297), // &igr -> &igra - new Transition (3317, 3318), // &iiot -> &iiota - new Transition (3330, 3332), // &Im -> &Ima - new Transition (3336, 3337), // &im -> &ima - new Transition (3346, 3347), // &Imagin -> &Imagina - new Transition (3357, 3358), // &imagp -> &imagpa - new Transition (3380, 3381), // &inc -> &inca - new Transition (3403, 3404), // &intc -> &intca - new Transition (3415, 3416), // &Integr -> &Integra - new Transition (3420, 3421), // &interc -> &interca - new Transition (3433, 3434), // &intl -> &intla - new Transition (3454, 3455), // &InvisibleComm -> &InvisibleComma - new Transition (3486, 3487), // &Iot -> &Iota - new Transition (3489, 3490), // &iot -> &iota - new Transition (3577, 3578), // &jm -> &jma - new Transition (3618, 3619), // &K -> &Ka - new Transition (3621, 3622), // &Kapp -> &Kappa - new Transition (3624, 3625), // &k -> &ka - new Transition (3627, 3628), // &kapp -> &kappa - new Transition (3692, 3705), // &l -> &la - new Transition (3693, 3694), // &lA -> &lAa - new Transition (3698, 3699), // &L -> &La - new Transition (3719, 3720), // &lagr -> &lagra - new Transition (3725, 3726), // &Lambd -> &Lambda - new Transition (3730, 3731), // &lambd -> &lambda - new Transition (3747, 3748), // &Lapl -> &Lapla - new Transition (3792, 3799), // &lat -> &lata - new Transition (3794, 3795), // &lAt -> &lAta - new Transition (3807, 3808), // &lB -> &lBa - new Transition (3812, 3813), // &lb -> &lba - new Transition (3821, 3822), // &lbr -> &lbra - new Transition (3837, 3838), // &Lc -> &Lca - new Transition (3843, 3844), // &lc -> &lca - new Transition (3870, 3871), // &ldc -> &ldca - new Transition (3881, 3882), // &ldrdh -> &ldrdha - new Transition (3887, 3888), // &ldrush -> &ldrusha - new Transition (3900, 3919), // &Left -> &Lefta - new Transition (3907, 3908), // &LeftAngleBr -> &LeftAngleBra - new Transition (3926, 3927), // &left -> &lefta - new Transition (3933, 3934), // &LeftArrowB -> &LeftArrowBa - new Transition (3948, 3949), // &leftarrowt -> &leftarrowta - new Transition (3968, 3969), // &LeftDoubleBr -> &LeftDoubleBra - new Transition (3994, 3995), // &LeftDownVectorB -> &LeftDownVectorBa - new Transition (4004, 4005), // &lefth -> &leftha - new Transition (4022, 4023), // &leftleft -> &leftlefta - new Transition (4045, 4046), // &Leftright -> &Leftrighta - new Transition (4056, 4057), // &leftright -> &leftrighta - new Transition (4065, 4066), // &leftrighth -> &leftrightha - new Transition (4078, 4079), // &leftrightsquig -> &leftrightsquiga - new Transition (4121, 4122), // &LeftTri -> &LeftTria - new Transition (4128, 4129), // &LeftTriangleB -> &LeftTriangleBa - new Transition (4134, 4135), // &LeftTriangleEqu -> &LeftTriangleEqua - new Transition (4168, 4169), // &LeftUpVectorB -> &LeftUpVectorBa - new Transition (4179, 4180), // &LeftVectorB -> &LeftVectorBa - new Transition (4192, 4193), // &leqsl -> &leqsla - new Transition (4215, 4216), // &less -> &lessa - new Transition (4242, 4243), // &LessEqu -> &LessEqua - new Transition (4247, 4248), // &LessEqualGre -> &LessEqualGrea - new Transition (4259, 4260), // &LessFullEqu -> &LessFullEqua - new Transition (4265, 4266), // &LessGre -> &LessGrea - new Transition (4285, 4286), // &LessSl -> &LessSla - new Transition (4291, 4292), // &LessSlantEqu -> &LessSlantEqua - new Transition (4321, 4322), // &lH -> &lHa - new Transition (4325, 4326), // &lh -> &lha - new Transition (4348, 4350), // &ll -> &lla - new Transition (4363, 4364), // &Lleft -> &Llefta - new Transition (4370, 4371), // &llh -> &llha - new Transition (4394, 4396), // &lmoust -> &lmousta - new Transition (4401, 4402), // &ln -> &lna - new Transition (4422, 4423), // &lo -> &loa - new Transition (4450, 4451), // &Longleft -> &Longlefta - new Transition (4462, 4463), // &longleft -> &longlefta - new Transition (4484, 4485), // &Longleftright -> &Longleftrighta - new Transition (4495, 4496), // &longleftright -> &longleftrighta - new Transition (4502, 4503), // &longm -> &longma - new Transition (4524, 4525), // &Longright -> &Longrighta - new Transition (4535, 4536), // &longright -> &longrighta - new Transition (4543, 4544), // &loop -> &loopa - new Transition (4560, 4561), // &lop -> &lopa - new Transition (4579, 4580), // &low -> &lowa - new Transition (4584, 4585), // &lowb -> &lowba - new Transition (4621, 4622), // &lp -> &lpa - new Transition (4628, 4629), // &lr -> &lra - new Transition (4640, 4641), // &lrh -> &lrha - new Transition (4652, 4653), // &ls -> &lsa - new Transition (4720, 4721), // <l -> <la - new Transition (4738, 4739), // <rP -> <rPa - new Transition (4746, 4747), // &lurdsh -> &lurdsha - new Transition (4751, 4752), // &luruh -> &luruha - new Transition (4767, 4768), // &m -> &ma - new Transition (4781, 4782), // &M -> &Ma - new Transition (4812, 4813), // &mcomm -> &mcomma - new Transition (4820, 4821), // &md -> &mda - new Transition (4830, 4831), // &me -> &mea - new Transition (4836, 4837), // &measured -> &measureda - new Transition (4849, 4850), // &MediumSp -> &MediumSpa - new Transition (4876, 4878), // &mid -> &mida - new Transition (4957, 4958), // &multim -> &multima - new Transition (4961, 4962), // &mum -> &muma - new Transition (4965, 4966), // &n -> &na - new Transition (4968, 4969), // &nabl -> &nabla - new Transition (4971, 4972), // &N -> &Na - new Transition (5003, 5005), // &natur -> &natura - new Transition (5020, 5021), // &nc -> &nca - new Transition (5024, 5025), // &Nc -> &Nca - new Transition (5059, 5060), // &nd -> &nda - new Transition (5064, 5066), // &ne -> &nea - new Transition (5085, 5086), // &Neg -> &Nega - new Transition (5098, 5099), // &NegativeMediumSp -> &NegativeMediumSpa - new Transition (5109, 5110), // &NegativeThickSp -> &NegativeThickSpa - new Transition (5116, 5117), // &NegativeThinSp -> &NegativeThinSpa - new Transition (5130, 5131), // &NegativeVeryThinSp -> &NegativeVeryThinSpa - new Transition (5141, 5142), // &nese -> &nesea - new Transition (5154, 5155), // &NestedGre -> &NestedGrea - new Transition (5161, 5162), // &NestedGreaterGre -> &NestedGreaterGrea - new Transition (5205, 5206), // &ngeqsl -> &ngeqsla - new Transition (5227, 5232), // &nh -> &nha - new Transition (5236, 5237), // &nhp -> &nhpa - new Transition (5256, 5261), // &nl -> &nla - new Transition (5275, 5276), // &nLeft -> &nLefta - new Transition (5283, 5284), // &nleft -> &nlefta - new Transition (5294, 5295), // &nLeftright -> &nLeftrighta - new Transition (5305, 5306), // &nleftright -> &nleftrighta - new Transition (5317, 5318), // &nleqsl -> &nleqsla - new Transition (5350, 5351), // &NoBre -> &NoBrea - new Transition (5357, 5358), // &NonBre -> &NonBrea - new Transition (5364, 5365), // &NonBreakingSp -> &NonBreakingSpa - new Transition (5392, 5393), // &NotCupC -> &NotCupCa - new Transition (5407, 5408), // &NotDoubleVertic -> &NotDoubleVertica - new Transition (5410, 5411), // &NotDoubleVerticalB -> &NotDoubleVerticalBa - new Transition (5423, 5424), // &NotEqu -> &NotEqua - new Transition (5441, 5442), // &NotGre -> &NotGrea - new Transition (5449, 5450), // &NotGreaterEqu -> &NotGreaterEqua - new Transition (5459, 5460), // &NotGreaterFullEqu -> &NotGreaterFullEqua - new Transition (5465, 5466), // &NotGreaterGre -> &NotGreaterGrea - new Transition (5477, 5478), // &NotGreaterSl -> &NotGreaterSla - new Transition (5483, 5484), // &NotGreaterSlantEqu -> &NotGreaterSlantEqua - new Transition (5508, 5509), // &NotHumpEqu -> &NotHumpEqua - new Transition (5521, 5522), // ¬inv -> ¬inva - new Transition (5534, 5535), // &NotLeftTri -> &NotLeftTria - new Transition (5541, 5542), // &NotLeftTriangleB -> &NotLeftTriangleBa - new Transition (5547, 5548), // &NotLeftTriangleEqu -> &NotLeftTriangleEqua - new Transition (5556, 5557), // &NotLessEqu -> &NotLessEqua - new Transition (5562, 5563), // &NotLessGre -> &NotLessGrea - new Transition (5574, 5575), // &NotLessSl -> &NotLessSla - new Transition (5580, 5581), // &NotLessSlantEqu -> &NotLessSlantEqua - new Transition (5598, 5599), // &NotNestedGre -> &NotNestedGrea - new Transition (5605, 5606), // &NotNestedGreaterGre -> &NotNestedGreaterGrea - new Transition (5623, 5624), // ¬niv -> ¬niva - new Transition (5641, 5642), // &NotPrecedesEqu -> &NotPrecedesEqua - new Transition (5646, 5647), // &NotPrecedesSl -> &NotPrecedesSla - new Transition (5652, 5653), // &NotPrecedesSlantEqu -> &NotPrecedesSlantEqua - new Transition (5677, 5678), // &NotRightTri -> &NotRightTria - new Transition (5684, 5685), // &NotRightTriangleB -> &NotRightTriangleBa - new Transition (5690, 5691), // &NotRightTriangleEqu -> &NotRightTriangleEqua - new Transition (5696, 5697), // &NotSqu -> &NotSqua - new Transition (5709, 5710), // &NotSquareSubsetEqu -> &NotSquareSubsetEqua - new Transition (5722, 5723), // &NotSquareSupersetEqu -> &NotSquareSupersetEqua - new Transition (5734, 5735), // &NotSubsetEqu -> &NotSubsetEqua - new Transition (5747, 5748), // &NotSucceedsEqu -> &NotSucceedsEqua - new Transition (5752, 5753), // &NotSucceedsSl -> &NotSucceedsSla - new Transition (5758, 5759), // &NotSucceedsSlantEqu -> &NotSucceedsSlantEqua - new Transition (5777, 5778), // &NotSupersetEqu -> &NotSupersetEqua - new Transition (5789, 5790), // &NotTildeEqu -> &NotTildeEqua - new Transition (5799, 5800), // &NotTildeFullEqu -> &NotTildeFullEqua - new Transition (5814, 5815), // &NotVertic -> &NotVertica - new Transition (5817, 5818), // &NotVerticalB -> &NotVerticalBa - new Transition (5821, 5822), // &np -> &npa - new Transition (5823, 5825), // &npar -> &npara - new Transition (5855, 5860), // &nr -> &nra - new Transition (5872, 5873), // &nRight -> &nRighta - new Transition (5882, 5883), // &nright -> &nrighta - new Transition (5918, 5919), // &nshortp -> &nshortpa - new Transition (5920, 5921), // &nshortpar -> &nshortpara - new Transition (5938, 5939), // &nsp -> &nspa - new Transition (6007, 6008), // &ntri -> &ntria - new Transition (6043, 6044), // &nv -> &nva - new Transition (6048, 6049), // &nVD -> &nVDa - new Transition (6053, 6054), // &nVd -> &nVda - new Transition (6058, 6059), // &nvD -> &nvDa - new Transition (6063, 6064), // &nvd -> &nvda - new Transition (6073, 6074), // &nvH -> &nvHa - new Transition (6111, 6112), // &nw -> &nwa - new Transition (6127, 6128), // &nwne -> &nwnea - new Transition (6131, 6132), // &O -> &Oa - new Transition (6138, 6139), // &o -> &oa - new Transition (6163, 6164), // &od -> &oda - new Transition (6170, 6171), // &Odbl -> &Odbla - new Transition (6175, 6176), // &odbl -> &odbla - new Transition (6215, 6216), // &Ogr -> &Ogra - new Transition (6220, 6221), // &ogr -> &ogra - new Transition (6228, 6229), // &ohb -> &ohba - new Transition (6238, 6239), // &ol -> &ola - new Transition (6258, 6259), // &Om -> &Oma - new Transition (6263, 6264), // &om -> &oma - new Transition (6269, 6270), // &Omeg -> &Omega - new Transition (6273, 6274), // &omeg -> &omega - new Transition (6302, 6303), // &op -> &opa - new Transition (6342, 6344), // &or -> &ora - new Transition (6386, 6387), // &Osl -> &Osla - new Transition (6391, 6392), // &osl -> &osla - new Transition (6417, 6419), // &otimes -> &otimesa - new Transition (6431, 6432), // &ovb -> &ovba - new Transition (6438, 6439), // &OverB -> &OverBa - new Transition (6442, 6443), // &OverBr -> &OverBra - new Transition (6451, 6452), // &OverP -> &OverPa - new Transition (6463, 6464), // &p -> &pa - new Transition (6465, 6467), // &par -> ¶ - new Transition (6482, 6483), // &P -> &Pa - new Transition (6486, 6487), // &Parti -> &Partia - new Transition (6533, 6534), // &phmm -> &phmma - new Transition (6555, 6556), // &pl -> &pla - new Transition (6567, 6569), // &plus -> &plusa - new Transition (6612, 6613), // &Poinc -> &Poinca - new Transition (6617, 6618), // &Poincarepl -> &Poincarepla - new Transition (6642, 6644), // &pr -> &pra - new Transition (6655, 6657), // &prec -> &preca - new Transition (6681, 6682), // &PrecedesEqu -> &PrecedesEqua - new Transition (6686, 6687), // &PrecedesSl -> &PrecedesSla - new Transition (6692, 6693), // &PrecedesSlantEqu -> &PrecedesSlantEqua - new Transition (6705, 6706), // &precn -> &precna - new Transition (6735, 6736), // &prn -> &prna - new Transition (6754, 6755), // &prof -> &profa - new Transition (6756, 6757), // &profal -> &profala - new Transition (6778, 6780), // &Proportion -> &Proportiona - new Transition (6847, 6848), // &qu -> &qua - new Transition (6876, 6882), // &r -> &ra - new Transition (6877, 6878), // &rA -> &rAa - new Transition (6886, 6887), // &R -> &Ra - new Transition (6932, 6934), // &rarr -> &rarra - new Transition (6968, 6969), // &rAt -> &rAta - new Transition (6973, 6974), // &rat -> &rata - new Transition (6981, 6982), // &ration -> &rationa - new Transition (6986, 6987), // &RB -> &RBa - new Transition (6991, 6992), // &rB -> &rBa - new Transition (6996, 6997), // &rb -> &rba - new Transition (7005, 7006), // &rbr -> &rbra - new Transition (7021, 7022), // &Rc -> &Rca - new Transition (7027, 7028), // &rc -> &rca - new Transition (7054, 7055), // &rdc -> &rdca - new Transition (7059, 7060), // &rdldh -> &rdldha - new Transition (7074, 7075), // &re -> &rea - new Transition (7082, 7083), // &realp -> &realpa - new Transition (7151, 7152), // &rH -> &rHa - new Transition (7155, 7156), // &rh -> &rha - new Transition (7174, 7193), // &Right -> &Righta - new Transition (7181, 7182), // &RightAngleBr -> &RightAngleBra - new Transition (7202, 7203), // &right -> &righta - new Transition (7209, 7210), // &RightArrowB -> &RightArrowBa - new Transition (7223, 7224), // &rightarrowt -> &rightarrowta - new Transition (7243, 7244), // &RightDoubleBr -> &RightDoubleBra - new Transition (7269, 7270), // &RightDownVectorB -> &RightDownVectorBa - new Transition (7279, 7280), // &righth -> &rightha - new Transition (7297, 7298), // &rightleft -> &rightlefta - new Transition (7305, 7306), // &rightlefth -> &rightleftha - new Transition (7318, 7319), // &rightright -> &rightrighta - new Transition (7330, 7331), // &rightsquig -> &rightsquiga - new Transition (7366, 7367), // &RightTri -> &RightTria - new Transition (7373, 7374), // &RightTriangleB -> &RightTriangleBa - new Transition (7379, 7380), // &RightTriangleEqu -> &RightTriangleEqua - new Transition (7413, 7414), // &RightUpVectorB -> &RightUpVectorBa - new Transition (7424, 7425), // &RightVectorB -> &RightVectorBa - new Transition (7442, 7443), // &rl -> &rla - new Transition (7447, 7448), // &rlh -> &rlha - new Transition (7457, 7459), // &rmoust -> &rmousta - new Transition (7469, 7470), // &ro -> &roa - new Transition (7481, 7482), // &rop -> &ropa - new Transition (7512, 7513), // &rp -> &rpa - new Transition (7526, 7527), // &rr -> &rra - new Transition (7535, 7536), // &Rright -> &Rrighta - new Transition (7542, 7543), // &rs -> &rsa - new Transition (7595, 7596), // &RuleDel -> &RuleDela - new Transition (7604, 7605), // &ruluh -> &ruluha - new Transition (7610, 7611), // &S -> &Sa - new Transition (7617, 7618), // &s -> &sa - new Transition (7629, 7636), // &Sc -> &Sca - new Transition (7631, 7633), // &sc -> &sca - new Transition (7670, 7671), // &scn -> &scna - new Transition (7703, 7704), // &se -> &sea - new Transition (7725, 7726), // &sesw -> &seswa - new Transition (7751, 7752), // &sh -> &sha - new Transition (7803, 7804), // &shortp -> &shortpa - new Transition (7805, 7806), // &shortpar -> &shortpara - new Transition (7835, 7836), // &Sigm -> &Sigma - new Transition (7840, 7841), // &sigm -> &sigma - new Transition (7873, 7874), // &simr -> &simra - new Transition (7878, 7879), // &sl -> &sla - new Transition (7883, 7884), // &Sm -> &Sma - new Transition (7894, 7895), // &sm -> &sma - new Transition (7912, 7913), // &smep -> &smepa - new Transition (7944, 7946), // &solb -> &solba - new Transition (7956, 7957), // &sp -> &spa - new Transition (7969, 7970), // &sqc -> &sqca - new Transition (8008, 8015), // &squ -> &squa - new Transition (8010, 8011), // &Squ -> &Squa - new Transition (8041, 8042), // &SquareSubsetEqu -> &SquareSubsetEqua - new Transition (8054, 8055), // &SquareSupersetEqu -> &SquareSupersetEqua - new Transition (8068, 8069), // &sr -> &sra - new Transition (8091, 8092), // &sst -> &ssta - new Transition (8096, 8097), // &St -> &Sta - new Transition (8100, 8101), // &st -> &sta - new Transition (8106, 8107), // &str -> &stra - new Transition (8160, 8161), // &subr -> &subra - new Transition (8180, 8181), // &SubsetEqu -> &SubsetEqua - new Transition (8199, 8201), // &succ -> &succa - new Transition (8225, 8226), // &SucceedsEqu -> &SucceedsEqua - new Transition (8230, 8231), // &SucceedsSl -> &SucceedsSla - new Transition (8236, 8237), // &SucceedsSlantEqu -> &SucceedsSlantEqua - new Transition (8249, 8250), // &succn -> &succna - new Transition (8271, 8272), // &SuchTh -> &SuchTha - new Transition (8316, 8317), // &SupersetEqu -> &SupersetEqua - new Transition (8328, 8329), // &supl -> &supla - new Transition (8375, 8376), // &sw -> &swa - new Transition (8391, 8392), // &swnw -> &swnwa - new Transition (8400, 8401), // &T -> &Ta - new Transition (8404, 8405), // &t -> &ta - new Transition (8419, 8420), // &Tc -> &Tca - new Transition (8425, 8426), // &tc -> &tca - new Transition (8481, 8482), // &Thet -> &Theta - new Transition (8484, 8485), // &thet -> &theta - new Transition (8495, 8496), // &thick -> &thicka - new Transition (8511, 8512), // &ThickSp -> &ThickSpa - new Transition (8522, 8523), // &ThinSp -> &ThinSpa - new Transition (8527, 8528), // &thk -> &thka - new Transition (8556, 8557), // &TildeEqu -> &TildeEqua - new Transition (8566, 8567), // &TildeFullEqu -> &TildeFullEqua - new Transition (8580, 8582), // ×b -> ×ba - new Transition (8591, 8592), // &toe -> &toea - new Transition (8614, 8615), // &tos -> &tosa - new Transition (8628, 8629), // &tr -> &tra - new Transition (8633, 8634), // &tri -> &tria - new Transition (8744, 8745), // &twohe -> &twohea - new Transition (8750, 8751), // &twoheadleft -> &twoheadlefta - new Transition (8761, 8762), // &twoheadright -> &twoheadrighta - new Transition (8768, 8769), // &U -> &Ua - new Transition (8775, 8776), // &u -> &ua - new Transition (8829, 8830), // &ud -> &uda - new Transition (8836, 8837), // &Udbl -> &Udbla - new Transition (8841, 8842), // &udbl -> &udbla - new Transition (8845, 8846), // &udh -> &udha - new Transition (8861, 8862), // &Ugr -> &Ugra - new Transition (8867, 8868), // &ugr -> &ugra - new Transition (8872, 8873), // &uH -> &uHa - new Transition (8876, 8877), // &uh -> &uha - new Transition (8904, 8905), // &Um -> &Uma - new Transition (8909, 8910), // &um -> &uma - new Transition (8920, 8921), // &UnderB -> &UnderBa - new Transition (8924, 8925), // &UnderBr -> &UnderBra - new Transition (8933, 8934), // &UnderP -> &UnderPa - new Transition (8970, 8977), // &Up -> &Upa - new Transition (8983, 8984), // &up -> &upa - new Transition (8990, 8991), // &UpArrowB -> &UpArrowBa - new Transition (9017, 9018), // &Updown -> &Updowna - new Transition (9027, 9028), // &updown -> &updowna - new Transition (9046, 9047), // &uph -> &upha - new Transition (9119, 9120), // &upup -> &upupa - new Transition (9182, 9183), // &uu -> &uua - new Transition (9194, 9195), // &uw -> &uwa - new Transition (9201, 9202), // &v -> &va - new Transition (9217, 9218), // &vark -> &varka - new Transition (9220, 9221), // &varkapp -> &varkappa - new Transition (9255, 9256), // &varsigm -> &varsigma - new Transition (9282, 9283), // &varthet -> &vartheta - new Transition (9286, 9287), // &vartri -> &vartria - new Transition (9304, 9305), // &Vb -> &Vba - new Transition (9308, 9309), // &vB -> &vBa - new Transition (9320, 9321), // &VD -> &VDa - new Transition (9325, 9326), // &Vd -> &Vda - new Transition (9330, 9331), // &vD -> &vDa - new Transition (9335, 9336), // &vd -> &vda - new Transition (9348, 9349), // &veeb -> &veeba - new Transition (9361, 9362), // &Verb -> &Verba - new Transition (9366, 9367), // &verb -> &verba - new Transition (9375, 9376), // &Vertic -> &Vertica - new Transition (9378, 9379), // &VerticalB -> &VerticalBa - new Transition (9389, 9390), // &VerticalSep -> &VerticalSepa - new Transition (9391, 9392), // &VerticalSepar -> &VerticalSepara - new Transition (9409, 9410), // &VeryThinSp -> &VeryThinSpa - new Transition (9472, 9473), // &Vvd -> &Vvda - new Transition (9480, 9481), // &vzigz -> &vzigza - new Transition (9498, 9499), // &wedb -> &wedba - new Transition (9535, 9536), // &wre -> &wrea - new Transition (9549, 9550), // &xc -> &xca - new Transition (9572, 9577), // &xh -> &xha - new Transition (9585, 9590), // &xl -> &xla - new Transition (9594, 9595), // &xm -> &xma - new Transition (9623, 9628), // &xr -> &xra - new Transition (9665, 9666), // &Y -> &Ya - new Transition (9672, 9673), // &y -> &ya - new Transition (9747, 9748), // &Z -> &Za - new Transition (9754, 9755), // &z -> &za - new Transition (9761, 9762), // &Zc -> &Zca - new Transition (9767, 9768), // &zc -> &zca - new Transition (9800, 9801), // &ZeroWidthSp -> &ZeroWidthSpa - new Transition (9805, 9806), // &Zet -> &Zeta - new Transition (9808, 9809), // &zet -> &zeta - new Transition (9827, 9828) // &zigr -> &zigra - }; - TransitionTable_b = new Transition[96] { - new Transition (0, 301), // & -> &b - new Transition (1, 15), // &A -> &Ab - new Transition (8, 21), // &a -> &ab - new Transition (147, 150), // &angmsda -> &angmsdab - new Transition (167, 168), // &angrtv -> &angrtvb - new Transition (301, 360), // &b -> &bb - new Transition (364, 365), // &bbrkt -> &bbrktb - new Transition (613, 614), // &box -> &boxb - new Transition (735, 736), // &brv -> &brvb - new Transition (758, 760), // &bsol -> &bsolb - new Transition (764, 765), // &bsolhsu -> &bsolhsub - new Transition (805, 811), // &cap -> &capb - new Transition (1101, 1102), // &CloseCurlyDou -> &CloseCurlyDoub - new Transition (1118, 1119), // &clu -> &club - new Transition (1278, 1279), // &csu -> &csub - new Transition (1318, 1320), // &cup -> &cupb - new Transition (1432, 1463), // &d -> &db - new Transition (1577, 1578), // &DiacriticalDou -> &DiacriticalDoub - new Transition (1731, 1732), // &dou -> &doub - new Transition (1734, 1735), // &double -> &doubleb - new Transition (1744, 1745), // &Dou -> &Doub - new Transition (2023, 2024), // &dr -> &drb - new Transition (2389, 2390), // &Equili -> &Equilib - new Transition (2701, 2730), // &g -> &gb - new Transition (2708, 2724), // &G -> &Gb - new Transition (3020, 3060), // &h -> &hb - new Transition (3101, 3102), // &Hil -> &Hilb - new Transition (3166, 3167), // &hor -> &horb - new Transition (3225, 3226), // &hy -> &hyb - new Transition (3447, 3448), // &Invisi -> &Invisib - new Transition (3692, 3812), // &l -> &lb - new Transition (3723, 3724), // &Lam -> &Lamb - new Transition (3728, 3729), // &lam -> &lamb - new Transition (3766, 3768), // &larr -> &larrb - new Transition (3812, 3817), // &lb -> &lbb - new Transition (3862, 3863), // &lcu -> &lcub - new Transition (3963, 3964), // &LeftDou -> &LeftDoub - new Transition (4325, 4334), // &lh -> &lhb - new Transition (4422, 4430), // &lo -> &lob - new Transition (4579, 4584), // &low -> &lowb - new Transition (4676, 4677), // &lsq -> &lsqb - new Transition (4892, 4894), // &minus -> &minusb - new Transition (4965, 5010), // &n -> &nb - new Transition (4966, 4967), // &na -> &nab - new Transition (5398, 5399), // &NotDou -> &NotDoub - new Transition (5521, 5524), // ¬inv -> ¬invb - new Transition (5623, 5626), // ¬niv -> ¬nivb - new Transition (5701, 5702), // &NotSquareSu -> &NotSquareSub - new Transition (5726, 5727), // &NotSu -> &NotSub - new Transition (5944, 5945), // &nsqsu -> &nsqsub - new Transition (5951, 5952), // &nsu -> &nsub - new Transition (6163, 6174), // &od -> &odb - new Transition (6168, 6169), // &Od -> &Odb - new Transition (6227, 6228), // &oh -> &ohb - new Transition (6316, 6317), // &OpenCurlyDou -> &OpenCurlyDoub - new Transition (6430, 6431), // &ov -> &ovb - new Transition (6567, 6574), // &plus -> &plusb - new Transition (6876, 6996), // &r -> &rb - new Transition (6932, 6937), // &rarr -> &rarrb - new Transition (6996, 7001), // &rb -> &rbb - new Transition (7046, 7047), // &rcu -> &rcub - new Transition (7114, 7115), // &ReverseEquili -> &ReverseEquilib - new Transition (7128, 7129), // &ReverseUpEquili -> &ReverseUpEquilib - new Transition (7238, 7239), // &RightDou -> &RightDoub - new Transition (7469, 7477), // &ro -> &rob - new Transition (7559, 7560), // &rsq -> &rsqb - new Transition (7617, 7624), // &s -> &sb - new Transition (7697, 7699), // &sdot -> &sdotb - new Transition (7942, 7944), // &sol -> &solb - new Transition (7985, 7986), // &sqsu -> &sqsub - new Transition (8033, 8034), // &SquareSu -> &SquareSub - new Transition (8127, 8128), // &Su -> &Sub - new Transition (8130, 8131), // &su -> &sub - new Transition (8193, 8194), // &subsu -> &subsub - new Transition (8297, 8298), // &supdsu -> &supdsub - new Transition (8325, 8326), // &suphsu -> &suphsub - new Transition (8370, 8371), // &supsu -> &supsub - new Transition (8401, 8402), // &Ta -> &Tab - new Transition (8404, 8415), // &t -> &tb - new Transition (8578, 8580), // × -> ×b - new Transition (8594, 8596), // &top -> &topb - new Transition (8690, 8691), // &tris -> &trisb - new Transition (8768, 8797), // &U -> &Ub - new Transition (8775, 8802), // &u -> &ub - new Transition (8829, 8840), // &ud -> &udb - new Transition (8834, 8835), // &Ud -> &Udb - new Transition (8876, 8883), // &uh -> &uhb - new Transition (9039, 9040), // &UpEquili -> &UpEquilib - new Transition (9258, 9259), // &varsu -> &varsub - new Transition (9303, 9304), // &V -> &Vb - new Transition (9346, 9348), // &vee -> &veeb - new Transition (9360, 9361), // &Ver -> &Verb - new Transition (9365, 9366), // &ver -> &verb - new Transition (9427, 9428), // &vnsu -> &vnsub - new Transition (9458, 9459), // &vsu -> &vsub - new Transition (9497, 9498) // &wed -> &wedb - }; - TransitionTable_c = new Transition[377] { - new Transition (0, 796), // & -> &c - new Transition (1, 33), // &A -> &Ac - new Transition (2, 3), // &Aa -> &Aac - new Transition (8, 27), // &a -> &ac - new Transition (9, 10), // &aa -> &aac - new Transition (35, 36), // &Acir ->  - new Transition (39, 40), // &acir -> â - new Transition (99, 100), // &Ama -> &Amac - new Transition (104, 105), // &ama -> &amac - new Transition (147, 152), // &angmsda -> &angmsdac - new Transition (201, 202), // &apa -> &apac - new Transition (222, 223), // &ApplyFun -> &ApplyFunc - new Transition (247, 248), // &As -> &Asc - new Transition (251, 252), // &as -> &asc - new Transition (289, 290), // &aw -> &awc - new Transition (301, 369), // &b -> &bc - new Transition (302, 303), // &ba -> &bac - new Transition (304, 305), // &back -> &backc - new Transition (331, 374), // &B -> &Bc - new Transition (332, 333), // &Ba -> &Bac - new Transition (384, 385), // &be -> &bec - new Transition (390, 391), // &Be -> &Bec - new Transition (443, 444), // &big -> &bigc - new Transition (449, 450), // &bigcir -> &bigcirc - new Transition (472, 473), // &bigsq -> &bigsqc - new Transition (520, 521), // &bla -> &blac - new Transition (575, 576), // &blo -> &bloc - new Transition (740, 741), // &Bs -> &Bsc - new Transition (744, 745), // &bs -> &bsc - new Transition (789, 866), // &C -> &Cc - new Transition (790, 791), // &Ca -> &Cac - new Transition (796, 861), // &c -> &cc - new Transition (797, 798), // &ca -> &cac - new Transition (805, 817), // &cap -> &capc - new Transition (812, 813), // &capbr -> &capbrc - new Transition (887, 888), // &Ccir -> &Ccirc - new Transition (891, 892), // &ccir -> &ccirc - new Transition (956, 957), // &CH -> &CHc - new Transition (960, 961), // &ch -> &chc - new Transition (964, 965), // &che -> &chec - new Transition (979, 981), // &cir -> &circ - new Transition (1004, 1009), // &circled -> &circledc - new Transition (1011, 1012), // &circledcir -> &circledcirc - new Transition (1020, 1021), // &Cir -> &Circ - new Transition (1063, 1064), // &cirs -> &cirsc - new Transition (1069, 1070), // &Clo -> &Cloc - new Transition (1213, 1214), // &Coprodu -> &Coproduc - new Transition (1233, 1234), // &CounterClo -> &CounterCloc - new Transition (1270, 1271), // &Cs -> &Csc - new Transition (1274, 1275), // &cs -> &csc - new Transition (1305, 1306), // &cues -> &cuesc - new Transition (1318, 1330), // &cup -> &cupc - new Transition (1321, 1322), // &cupbr -> &cupbrc - new Transition (1359, 1360), // &curlyeqpre -> &curlyeqprec - new Transition (1363, 1364), // &curlyeqsu -> &curlyeqsuc - new Transition (1364, 1365), // &curlyeqsuc -> &curlyeqsucc - new Transition (1407, 1408), // &cw -> &cwc - new Transition (1420, 1421), // &cyl -> &cylc - new Transition (1425, 1474), // &D -> &Dc - new Transition (1432, 1480), // &d -> &dc - new Transition (1471, 1472), // &dbla -> &dblac - new Transition (1558, 1559), // &Dia -> &Diac - new Transition (1563, 1564), // &Diacriti -> &Diacritic - new Transition (1567, 1568), // &DiacriticalA -> &DiacriticalAc - new Transition (1581, 1582), // &DiacriticalDoubleA -> &DiacriticalDoubleAc - new Transition (1661, 1662), // &DJ -> &DJc - new Transition (1665, 1666), // &dj -> &djc - new Transition (1669, 1670), // &dl -> &dlc - new Transition (1873, 1874), // &DoubleVerti -> &DoubleVertic - new Transition (1960, 1961), // &DownLeftRightVe -> &DownLeftRightVec - new Transition (1970, 1971), // &DownLeftTeeVe -> &DownLeftTeeVec - new Transition (1977, 1978), // &DownLeftVe -> &DownLeftVec - new Transition (1996, 1997), // &DownRightTeeVe -> &DownRightTeeVec - new Transition (2003, 2004), // &DownRightVe -> &DownRightVec - new Transition (2023, 2031), // &dr -> &drc - new Transition (2040, 2041), // &Ds -> &Dsc - new Transition (2044, 2045), // &ds -> &dsc - new Transition (2048, 2049), // &DS -> &DSc - new Transition (2093, 2094), // &DZ -> &DZc - new Transition (2097, 2098), // &dz -> &dzc - new Transition (2108, 2127), // &E -> &Ec - new Transition (2109, 2110), // &Ea -> &Eac - new Transition (2115, 2133), // &e -> &ec - new Transition (2116, 2117), // &ea -> &eac - new Transition (2140, 2146), // &ecir -> ê - new Transition (2143, 2144), // &Ecir -> Ê - new Transition (2229, 2230), // &Ema -> &Emac - new Transition (2234, 2235), // &ema -> &emac - new Transition (2339, 2340), // &eq -> &eqc - new Transition (2342, 2343), // &eqcir -> &eqcirc - new Transition (2418, 2419), // &Es -> &Esc - new Transition (2422, 2423), // &es -> &esc - new Transition (2458, 2459), // &ex -> &exc - new Transition (2473, 2474), // &expe -> &expec - new Transition (2503, 2521), // &f -> &fc - new Transition (2517, 2518), // &F -> &Fc - new Transition (2648, 2649), // &fra -> &frac - new Transition (2693, 2694), // &Fs -> &Fsc - new Transition (2697, 2698), // &fs -> &fsc - new Transition (2701, 2746), // &g -> &gc - new Transition (2702, 2703), // &ga -> &gac - new Transition (2708, 2736), // &G -> &Gc - new Transition (2743, 2744), // &Gcir -> &Gcirc - new Transition (2748, 2749), // &gcir -> &gcirc - new Transition (2781, 2783), // &ges -> &gesc - new Transition (2783, 2784), // &gesc -> &gescc - new Transition (2816, 2817), // &GJ -> &GJc - new Transition (2820, 2821), // &gj -> &gjc - new Transition (2923, 2924), // &Gs -> &Gsc - new Transition (2927, 2928), // &gs -> &gsc - new Transition (2942, 2944), // > -> >c - new Transition (2944, 2945), // >c -> >cc - new Transition (3014, 3064), // &H -> &Hc - new Transition (3015, 3016), // &Ha -> &Hac - new Transition (3020, 3069), // &h -> &hc - new Transition (3037, 3038), // &HARD -> &HARDc - new Transition (3042, 3043), // &hard -> &hardc - new Transition (3050, 3052), // &harr -> &harrc - new Transition (3066, 3067), // &Hcir -> &Hcirc - new Transition (3071, 3072), // &hcir -> &hcirc - new Transition (3089, 3090), // &her -> &herc - new Transition (3108, 3109), // &HilbertSpa -> &HilbertSpac - new Transition (3184, 3185), // &Hs -> &Hsc - new Transition (3188, 3189), // &hs -> &hsc - new Transition (3236, 3252), // &I -> &Ic - new Transition (3237, 3238), // &Ia -> &Iac - new Transition (3243, 3250), // &i -> &ic - new Transition (3244, 3245), // &ia -> &iac - new Transition (3254, 3255), // &Icir -> Î - new Transition (3258, 3259), // &icir -> î - new Transition (3269, 3270), // &IE -> &IEc - new Transition (3273, 3274), // &ie -> &iec - new Transition (3277, 3278), // &iex -> &iexc - new Transition (3332, 3333), // &Ima -> &Imac - new Transition (3337, 3338), // &ima -> &imac - new Transition (3378, 3380), // &in -> &inc - new Transition (3401, 3403), // &int -> &intc - new Transition (3419, 3420), // &inter -> &interc - new Transition (3426, 3427), // &Interse -> &Intersec - new Transition (3463, 3464), // &IO -> &IOc - new Transition (3467, 3468), // &io -> &ioc - new Transition (3503, 3504), // &Is -> &Isc - new Transition (3507, 3508), // &is -> &isc - new Transition (3540, 3541), // &Iuk -> &Iukc - new Transition (3545, 3546), // &iuk -> &iukc - new Transition (3555, 3556), // &J -> &Jc - new Transition (3558, 3559), // &Jcir -> &Jcirc - new Transition (3561, 3562), // &j -> &jc - new Transition (3564, 3565), // &jcir -> &jcirc - new Transition (3590, 3591), // &Js -> &Jsc - new Transition (3594, 3595), // &js -> &jsc - new Transition (3599, 3600), // &Jser -> &Jserc - new Transition (3604, 3605), // &jser -> &jserc - new Transition (3609, 3610), // &Juk -> &Jukc - new Transition (3614, 3615), // &juk -> &jukc - new Transition (3618, 3632), // &K -> &Kc - new Transition (3624, 3638), // &k -> &kc - new Transition (3660, 3661), // &KH -> &KHc - new Transition (3664, 3665), // &kh -> &khc - new Transition (3668, 3669), // &KJ -> &KJc - new Transition (3672, 3673), // &kj -> &kjc - new Transition (3684, 3685), // &Ks -> &Ksc - new Transition (3688, 3689), // &ks -> &ksc - new Transition (3692, 3843), // &l -> &lc - new Transition (3698, 3837), // &L -> &Lc - new Transition (3699, 3700), // &La -> &Lac - new Transition (3705, 3706), // &la -> &lac - new Transition (3748, 3749), // &Lapla -> &Laplac - new Transition (3822, 3823), // &lbra -> &lbrac - new Transition (3869, 3870), // &ld -> &ldc - new Transition (3908, 3909), // &LeftAngleBra -> &LeftAngleBrac - new Transition (3969, 3970), // &LeftDoubleBra -> &LeftDoubleBrac - new Transition (3981, 3982), // &LeftDownTeeVe -> &LeftDownTeeVec - new Transition (3988, 3989), // &LeftDownVe -> &LeftDownVec - new Transition (4086, 4087), // &LeftRightVe -> &LeftRightVec - new Transition (4103, 4104), // &LeftTeeVe -> &LeftTeeVec - new Transition (4145, 4146), // &LeftUpDownVe -> &LeftUpDownVec - new Transition (4155, 4156), // &LeftUpTeeVe -> &LeftUpTeeVec - new Transition (4162, 4163), // &LeftUpVe -> &LeftUpVec - new Transition (4173, 4174), // &LeftVe -> &LeftVec - new Transition (4197, 4199), // &les -> &lesc - new Transition (4199, 4200), // &lesc -> &lescc - new Transition (4338, 4339), // &LJ -> &LJc - new Transition (4342, 4343), // &lj -> &ljc - new Transition (4348, 4354), // &ll -> &llc - new Transition (4396, 4397), // &lmousta -> &lmoustac - new Transition (4628, 4633), // &lr -> &lrc - new Transition (4652, 4662), // &ls -> &lsc - new Transition (4658, 4659), // &Ls -> &Lsc - new Transition (4698, 4700), // < -> <c - new Transition (4700, 4701), // <c -> <cc - new Transition (4767, 4809), // &m -> &mc - new Transition (4768, 4769), // &ma -> &mac - new Transition (4781, 4815), // &M -> &Mc - new Transition (4850, 4851), // &MediumSpa -> &MediumSpac - new Transition (4871, 4872), // &mi -> &mic - new Transition (4876, 4882), // &mid -> &midc - new Transition (4909, 4910), // &ml -> &mlc - new Transition (4937, 4938), // &Ms -> &Msc - new Transition (4941, 4942), // &ms -> &msc - new Transition (4965, 5020), // &n -> &nc - new Transition (4966, 4978), // &na -> &nac - new Transition (4971, 5024), // &N -> &Nc - new Transition (4972, 4973), // &Na -> &Nac - new Transition (5099, 5100), // &NegativeMediumSpa -> &NegativeMediumSpac - new Transition (5105, 5106), // &NegativeThi -> &NegativeThic - new Transition (5110, 5111), // &NegativeThickSpa -> &NegativeThickSpac - new Transition (5117, 5118), // &NegativeThinSpa -> &NegativeThinSpac - new Transition (5131, 5132), // &NegativeVeryThinSpa -> &NegativeVeryThinSpac - new Transition (5248, 5249), // &NJ -> &NJc - new Transition (5252, 5253), // &nj -> &njc - new Transition (5365, 5366), // &NonBreakingSpa -> &NonBreakingSpac - new Transition (5406, 5407), // &NotDoubleVerti -> &NotDoubleVertic - new Transition (5521, 5526), // ¬inv -> ¬invc - new Transition (5623, 5628), // ¬niv -> ¬nivc - new Transition (5632, 5633), // &NotPre -> &NotPrec - new Transition (5726, 5738), // &NotSu -> &NotSuc - new Transition (5738, 5739), // &NotSuc -> &NotSucc - new Transition (5813, 5814), // &NotVerti -> &NotVertic - new Transition (5842, 5844), // &npr -> &nprc - new Transition (5848, 5850), // &npre -> &nprec - new Transition (5862, 5864), // &nrarr -> &nrarrc - new Transition (5895, 5896), // &ns -> &nsc - new Transition (5896, 5898), // &nsc -> &nscc - new Transition (5904, 5905), // &Ns -> &Nsc - new Transition (5951, 5967), // &nsu -> &nsuc - new Transition (5967, 5968), // &nsuc -> &nsucc - new Transition (6131, 6152), // &O -> &Oc - new Transition (6132, 6133), // &Oa -> &Oac - new Transition (6138, 6148), // &o -> &oc - new Transition (6139, 6140), // &oa -> &oac - new Transition (6150, 6157), // &ocir -> ô - new Transition (6154, 6155), // &Ocir -> Ô - new Transition (6171, 6172), // &Odbla -> &Odblac - new Transition (6176, 6177), // &odbla -> &odblac - new Transition (6200, 6201), // &of -> &ofc - new Transition (6238, 6243), // &ol -> &olc - new Transition (6259, 6260), // &Oma -> &Omac - new Transition (6264, 6265), // &oma -> &omac - new Transition (6276, 6277), // &Omi -> &Omic - new Transition (6282, 6283), // &omi -> &omic - new Transition (6378, 6379), // &Os -> &Osc - new Transition (6382, 6383), // &os -> &osc - new Transition (6443, 6444), // &OverBra -> &OverBrac - new Transition (6463, 6494), // &p -> &pc - new Transition (6482, 6491), // &P -> &Pc - new Transition (6498, 6499), // &per -> &perc - new Transition (6545, 6546), // &pit -> &pitc - new Transition (6557, 6558), // &plan -> &planc - new Transition (6567, 6576), // &plus -> &plusc - new Transition (6569, 6570), // &plusa -> &plusac - new Transition (6611, 6612), // &Poin -> &Poinc - new Transition (6642, 6647), // &pr -> &prc - new Transition (6653, 6655), // &pre -> &prec - new Transition (6655, 6664), // &prec -> &precc - new Transition (6672, 6673), // &Pre -> &Prec - new Transition (6750, 6751), // &Produ -> &Produc - new Transition (6795, 6796), // &Ps -> &Psc - new Transition (6799, 6800), // &ps -> &psc - new Transition (6808, 6809), // &pun -> &punc - new Transition (6839, 6840), // &Qs -> &Qsc - new Transition (6843, 6844), // &qs -> &qsc - new Transition (6876, 7027), // &r -> &rc - new Transition (6882, 6883), // &ra -> &rac - new Transition (6886, 7021), // &R -> &Rc - new Transition (6887, 6888), // &Ra -> &Rac - new Transition (6898, 6899), // &radi -> &radic - new Transition (6932, 6942), // &rarr -> &rarrc - new Transition (7006, 7007), // &rbra -> &rbrac - new Transition (7053, 7054), // &rd -> &rdc - new Transition (7074, 7089), // &re -> &rec - new Transition (7182, 7183), // &RightAngleBra -> &RightAngleBrac - new Transition (7244, 7245), // &RightDoubleBra -> &RightDoubleBrac - new Transition (7256, 7257), // &RightDownTeeVe -> &RightDownTeeVec - new Transition (7263, 7264), // &RightDownVe -> &RightDownVec - new Transition (7348, 7349), // &RightTeeVe -> &RightTeeVec - new Transition (7390, 7391), // &RightUpDownVe -> &RightUpDownVec - new Transition (7400, 7401), // &RightUpTeeVe -> &RightUpTeeVec - new Transition (7407, 7408), // &RightUpVe -> &RightUpVec - new Transition (7418, 7419), // &RightVe -> &RightVec - new Transition (7459, 7460), // &rmousta -> &rmoustac - new Transition (7542, 7552), // &rs -> &rsc - new Transition (7548, 7549), // &Rs -> &Rsc - new Transition (7610, 7629), // &S -> &Sc - new Transition (7611, 7612), // &Sa -> &Sac - new Transition (7617, 7631), // &s -> &sc - new Transition (7618, 7619), // &sa -> &sac - new Transition (7631, 7645), // &sc -> &scc - new Transition (7663, 7664), // &Scir -> &Scirc - new Transition (7667, 7668), // &scir -> &scirc - new Transition (7703, 7718), // &se -> &sec - new Transition (7751, 7762), // &sh -> &shc - new Transition (7756, 7767), // &SH -> &SHc - new Transition (7758, 7759), // &SHCH -> &SHCHc - new Transition (7763, 7764), // &shch -> &shchc - new Transition (7889, 7890), // &SmallCir -> &SmallCirc - new Transition (7932, 7933), // &SOFT -> &SOFTc - new Transition (7938, 7939), // &soft -> &softc - new Transition (7968, 7969), // &sq -> &sqc - new Transition (8025, 8026), // &SquareInterse -> &SquareIntersec - new Transition (8073, 8074), // &Ss -> &Ssc - new Transition (8077, 8078), // &ss -> &ssc - new Transition (8127, 8216), // &Su -> &Suc - new Transition (8130, 8198), // &su -> &suc - new Transition (8198, 8199), // &suc -> &succ - new Transition (8199, 8208), // &succ -> &succc - new Transition (8216, 8217), // &Suc -> &Succ - new Transition (8400, 8419), // &T -> &Tc - new Transition (8404, 8425), // &t -> &tc - new Transition (8452, 8453), // &telre -> &telrec - new Transition (8493, 8494), // &thi -> &thic - new Transition (8507, 8508), // &Thi -> &Thic - new Transition (8512, 8513), // &ThickSpa -> &ThickSpac - new Transition (8523, 8524), // &ThinSpa -> &ThinSpac - new Transition (8594, 8600), // &top -> &topc - new Transition (8705, 8706), // &Ts -> &Tsc - new Transition (8709, 8710), // &ts -> &tsc - new Transition (8713, 8714), // &TS -> &TSc - new Transition (8719, 8720), // &TSH -> &TSHc - new Transition (8723, 8724), // &tsh -> &tshc - new Transition (8768, 8815), // &U -> &Uc - new Transition (8769, 8770), // &Ua -> &Uac - new Transition (8775, 8820), // &u -> &uc - new Transition (8776, 8777), // &ua -> &uac - new Transition (8792, 8793), // &Uarro -> &Uarroc - new Transition (8798, 8799), // &Ubr -> &Ubrc - new Transition (8803, 8804), // &ubr -> &ubrc - new Transition (8817, 8818), // &Ucir -> Û - new Transition (8822, 8823), // &ucir -> û - new Transition (8837, 8838), // &Udbla -> &Udblac - new Transition (8842, 8843), // &udbla -> &udblac - new Transition (8887, 8888), // &ul -> &ulc - new Transition (8905, 8906), // &Uma -> &Umac - new Transition (8910, 8911), // &uma -> &umac - new Transition (8925, 8926), // &UnderBra -> &UnderBrac - new Transition (9127, 9128), // &ur -> &urc - new Transition (9153, 9154), // &Us -> &Usc - new Transition (9157, 9158), // &us -> &usc - new Transition (9201, 9317), // &v -> &vc - new Transition (9303, 9314), // &V -> &Vc - new Transition (9374, 9375), // &Verti -> &Vertic - new Transition (9410, 9411), // &VeryThinSpa -> &VeryThinSpac - new Transition (9450, 9451), // &Vs -> &Vsc - new Transition (9454, 9455), // &vs -> &vsc - new Transition (9484, 9485), // &W -> &Wc - new Transition (9487, 9488), // &Wcir -> &Wcirc - new Transition (9490, 9491), // &w -> &wc - new Transition (9493, 9494), // &wcir -> &wcirc - new Transition (9540, 9541), // &Ws -> &Wsc - new Transition (9544, 9545), // &ws -> &wsc - new Transition (9548, 9549), // &x -> &xc - new Transition (9554, 9555), // &xcir -> &xcirc - new Transition (9632, 9633), // &Xs -> &Xsc - new Transition (9636, 9637), // &xs -> &xsc - new Transition (9640, 9641), // &xsq -> &xsqc - new Transition (9665, 9685), // &Y -> &Yc - new Transition (9666, 9667), // &Ya -> &Yac - new Transition (9672, 9690), // &y -> &yc - new Transition (9673, 9674), // &ya -> &yac - new Transition (9679, 9680), // &YA -> &YAc - new Transition (9687, 9688), // &Ycir -> &Ycirc - new Transition (9692, 9693), // &ycir -> &ycirc - new Transition (9708, 9709), // &YI -> &YIc - new Transition (9712, 9713), // &yi -> &yic - new Transition (9724, 9725), // &Ys -> &Ysc - new Transition (9728, 9729), // &ys -> &ysc - new Transition (9732, 9733), // &YU -> &YUc - new Transition (9736, 9737), // &yu -> &yuc - new Transition (9747, 9761), // &Z -> &Zc - new Transition (9748, 9749), // &Za -> &Zac - new Transition (9754, 9767), // &z -> &zc - new Transition (9755, 9756), // &za -> &zac - new Transition (9801, 9802), // &ZeroWidthSpa -> &ZeroWidthSpac - new Transition (9817, 9818), // &ZH -> &ZHc - new Transition (9821, 9822), // &zh -> &zhc - new Transition (9840, 9841), // &Zs -> &Zsc - new Transition (9844, 9845) // &zs -> &zsc - }; - TransitionTable_d = new Transition[206] { - new Transition (0, 1432), // & -> &d - new Transition (27, 29), // &ac -> &acd - new Transition (116, 117), // &An -> &And - new Transition (119, 120), // &an -> &and - new Transition (120, 126), // &and -> &andd - new Transition (123, 124), // &andan -> &andand - new Transition (144, 145), // &angms -> &angmsd - new Transition (147, 154), // &angmsda -> &angmsdad - new Transition (168, 170), // &angrtvb -> &angrtvbd - new Transition (210, 211), // &api -> &apid - new Transition (271, 272), // &Atil -> &Atild - new Transition (277, 278), // &atil -> &atild - new Transition (301, 379), // &b -> &bd - new Transition (350, 351), // &Barwe -> &Barwed - new Transition (354, 355), // &barwe -> &barwed - new Transition (455, 456), // &bigo -> &bigod - new Transition (488, 489), // &bigtriangle -> &bigtriangled - new Transition (508, 509), // &bigwe -> &bigwed - new Transition (545, 547), // &blacktriangle -> &blacktriangled - new Transition (613, 623), // &box -> &boxd - new Transition (636, 642), // &boxH -> &boxHd - new Transition (638, 646), // &boxh -> &boxhd - new Transition (789, 907), // &C -> &Cd - new Transition (796, 911), // &c -> &cd - new Transition (805, 824), // &cap -> &capd - new Transition (808, 809), // &capan -> &capand - new Transition (876, 877), // &Cce -> &Cced - new Transition (881, 882), // &cce -> &cced - new Transition (915, 916), // &ce -> &ced - new Transition (920, 921), // &Ce -> &Ced - new Transition (945, 946), // ¢er -> ¢erd - new Transition (987, 1004), // &circle -> &circled - new Transition (1004, 1014), // &circled -> &circledd - new Transition (1060, 1061), // &cirmi -> &cirmid - new Transition (1165, 1167), // &cong -> &congd - new Transition (1207, 1208), // &copro -> &coprod - new Transition (1211, 1212), // &Copro -> &Coprod - new Transition (1287, 1288), // &ct -> &ctd - new Transition (1292, 1293), // &cu -> &cud - new Transition (1318, 1337), // &cup -> &cupd - new Transition (1372, 1373), // &curlywe -> &curlywed - new Transition (1404, 1405), // &cuwe -> &cuwed - new Transition (1432, 1492), // &d -> &dd - new Transition (1507, 1508), // &DDotrah -> &DDotrahd - new Transition (1595, 1596), // &DiacriticalTil -> &DiacriticalTild - new Transition (1605, 1606), // &Diamon -> &Diamond - new Transition (1609, 1610), // &diamon -> &diamond - new Transition (1645, 1646), // &divi -> &divid - new Transition (1701, 1703), // &doteq -> &doteqd - new Transition (1739, 1740), // &doublebarwe -> &doublebarwed - new Transition (1896, 1921), // &down -> &downd - new Transition (2067, 2068), // &dt -> &dtd - new Transition (2108, 2162), // &E -> &Ed - new Transition (2115, 2169), // &e -> &ed - new Transition (2198, 2200), // &egs -> &egsd - new Transition (2222, 2224), // &els -> &elsd - new Transition (2379, 2380), // &EqualTil -> &EqualTild - new Transition (2422, 2426), // &es -> &esd - new Transition (2509, 2510), // &falling -> &fallingd - new Transition (2557, 2558), // &Fille -> &Filled - new Transition (2701, 2759), // &g -> &gd - new Transition (2708, 2755), // &G -> &Gd - new Transition (2712, 2718), // &Gamma -> &Gammad - new Transition (2716, 2720), // &gamma -> &gammad - new Transition (2737, 2738), // &Gce -> &Gced - new Transition (2781, 2786), // &ges -> &gesd - new Transition (2919, 2920), // &GreaterTil -> &GreaterTild - new Transition (2942, 2950), // > -> >d - new Transition (2965, 2976), // >r -> >rd - new Transition (3041, 3042), // &har -> &hard - new Transition (3236, 3265), // &I -> &Id - new Transition (3369, 3370), // &impe -> &imped - new Transition (3393, 3394), // &ino -> &inod - new Transition (3441, 3442), // &intpro -> &intprod - new Transition (3494, 3495), // &ipro -> &iprod - new Transition (3512, 3514), // &isin -> &isind - new Transition (3530, 3531), // &Itil -> &Itild - new Transition (3535, 3536), // &itil -> &itild - new Transition (3633, 3634), // &Kce -> &Kced - new Transition (3639, 3640), // &kce -> &kced - new Transition (3692, 3869), // &l -> &ld - new Transition (3724, 3725), // &Lamb -> &Lambd - new Transition (3729, 3730), // &lamb -> &lambd - new Transition (3737, 3739), // &lang -> &langd - new Transition (3832, 3833), // &lbrksl -> &lbrksld - new Transition (3849, 3850), // &Lce -> &Lced - new Transition (3854, 3855), // &lce -> &lced - new Transition (3879, 3880), // &ldr -> &ldrd - new Transition (4010, 4011), // &leftharpoon -> &leftharpoond - new Transition (4197, 4202), // &les -> &lesd - new Transition (4215, 4223), // &less -> &lessd - new Transition (4297, 4298), // &LessTil -> &LessTild - new Transition (4327, 4328), // &lhar -> &lhard - new Transition (4372, 4373), // &llhar -> &llhard - new Transition (4380, 4381), // &Lmi -> &Lmid - new Transition (4386, 4387), // &lmi -> &lmid - new Transition (4642, 4644), // &lrhar -> &lrhard - new Transition (4698, 4706), // < -> <d - new Transition (4743, 4744), // &lur -> &lurd - new Transition (4767, 4820), // &m -> &md - new Transition (4789, 4791), // &mapsto -> &mapstod - new Transition (4835, 4836), // &measure -> &measured - new Transition (4843, 4844), // &Me -> &Med - new Transition (4871, 4876), // &mi -> &mid - new Transition (4876, 4886), // &mid -> &midd - new Transition (4892, 4896), // &minus -> &minusd - new Transition (4909, 4913), // &ml -> &mld - new Transition (4922, 4923), // &mo -> &mod - new Transition (4965, 5059), // &n -> &nd - new Transition (4990, 4991), // &napi -> &napid - new Transition (5034, 5035), // &Nce -> &Nced - new Transition (5039, 5040), // &nce -> &nced - new Transition (5046, 5048), // &ncong -> &ncongd - new Transition (5064, 5080), // &ne -> &ned - new Transition (5092, 5093), // &NegativeMe -> &NegativeMed - new Transition (5150, 5151), // &Neste -> &Nested - new Transition (5242, 5244), // &nis -> &nisd - new Transition (5256, 5265), // &nl -> &nld - new Transition (5344, 5345), // &nmi -> &nmid - new Transition (5429, 5430), // &NotEqualTil -> &NotEqualTild - new Transition (5489, 5490), // &NotGreaterTil -> &NotGreaterTild - new Transition (5513, 5515), // ¬in -> ¬ind - new Transition (5586, 5587), // &NotLessTil -> &NotLessTild - new Transition (5594, 5595), // &NotNeste -> &NotNested - new Transition (5634, 5635), // &NotPrece -> &NotPreced - new Transition (5741, 5742), // &NotSuccee -> &NotSucceed - new Transition (5764, 5765), // &NotSucceedsTil -> &NotSucceedsTild - new Transition (5783, 5784), // &NotTil -> &NotTild - new Transition (5805, 5806), // &NotTildeTil -> &NotTildeTild - new Transition (5915, 5916), // &nshortmi -> &nshortmid - new Transition (5935, 5936), // &nsmi -> &nsmid - new Transition (5994, 5995), // &Ntil -> &Ntild - new Transition (5999, 6000), // &ntil -> &ntild - new Transition (6043, 6063), // &nv -> &nvd - new Transition (6047, 6053), // &nV -> &nVd - new Transition (6131, 6168), // &O -> &Od - new Transition (6138, 6163), // &o -> &od - new Transition (6187, 6188), // &odsol -> &odsold - new Transition (6282, 6288), // &omi -> &omid - new Transition (6342, 6348), // &or -> &ord - new Transition (6401, 6402), // &Otil -> &Otild - new Transition (6407, 6408), // &otil -> &otild - new Transition (6504, 6505), // &perio -> &period - new Transition (6567, 6580), // &plus -> &plusd - new Transition (6637, 6638), // &poun -> £ - new Transition (6674, 6675), // &Prece -> &Preced - new Transition (6698, 6699), // &PrecedesTil -> &PrecedesTild - new Transition (6745, 6746), // &pro -> &prod - new Transition (6748, 6749), // &Pro -> &Prod - new Transition (6876, 7053), // &r -> &rd - new Transition (6882, 6897), // &ra -> &rad - new Transition (6912, 6914), // &rang -> &rangd - new Transition (7016, 7017), // &rbrksl -> &rbrksld - new Transition (7033, 7034), // &Rce -> &Rced - new Transition (7038, 7039), // &rce -> &rced - new Transition (7057, 7058), // &rdl -> &rdld - new Transition (7157, 7158), // &rhar -> &rhard - new Transition (7285, 7286), // &rightharpoon -> &rightharpoond - new Transition (7434, 7435), // &rising -> &risingd - new Transition (7466, 7467), // &rnmi -> &rnmid - new Transition (7502, 7503), // &Roun -> &Round - new Transition (7598, 7599), // &RuleDelaye -> &RuleDelayed - new Transition (7617, 7695), // &s -> &sd - new Transition (7651, 7658), // &sce -> &sced - new Transition (7653, 7654), // &Sce -> &Sced - new Transition (7800, 7801), // &shortmi -> &shortmid - new Transition (7847, 7849), // &sim -> &simd - new Transition (7918, 7919), // &smi -> &smid - new Transition (7957, 7958), // &spa -> &spad - new Transition (8131, 8133), // &sub -> &subd - new Transition (8139, 8141), // &sube -> &subed - new Transition (8219, 8220), // &Succee -> &Succeed - new Transition (8242, 8243), // &SucceedsTil -> &SucceedsTild - new Transition (8284, 8292), // &sup -> &supd - new Transition (8302, 8304), // &supe -> &suped - new Transition (8404, 8445), // &t -> &td - new Transition (8431, 8432), // &Tce -> &Tced - new Transition (8436, 8437), // &tce -> &tced - new Transition (8545, 8546), // &Til -> &Tild - new Transition (8550, 8551), // &til -> &tild - new Transition (8572, 8573), // &TildeTil -> &TildeTild - new Transition (8578, 8585), // × -> ×d - new Transition (8629, 8630), // &tra -> &trad - new Transition (8633, 8664), // &tri -> &trid - new Transition (8638, 8640), // &triangle -> &triangled - new Transition (8745, 8746), // &twohea -> &twohead - new Transition (8768, 8834), // &U -> &Ud - new Transition (8775, 8829), // &u -> &ud - new Transition (8916, 8917), // &Un -> &Und - new Transition (8970, 9014), // &Up -> &Upd - new Transition (8983, 9024), // &up -> &upd - new Transition (9161, 9162), // &ut -> &utd - new Transition (9168, 9169), // &Util -> &Utild - new Transition (9173, 9174), // &util -> &utild - new Transition (9201, 9335), // &v -> &vd - new Transition (9303, 9325), // &V -> &Vd - new Transition (9399, 9400), // &VerticalTil -> &VerticalTild - new Transition (9471, 9472), // &Vv -> &Vvd - new Transition (9496, 9497), // &we -> &wed - new Transition (9502, 9503), // &We -> &Wed - new Transition (9548, 9560), // &x -> &xd - new Transition (9602, 9603), // &xo -> &xod - new Transition (9660, 9661), // &xwe -> &xwed - new Transition (9747, 9777), // &Z -> &Zd - new Transition (9754, 9781), // &z -> &zd - new Transition (9795, 9796) // &ZeroWi -> &ZeroWid - }; - TransitionTable_e = new Transition[674] { - new Transition (0, 2115), // & -> &e - new Transition (5, 6), // &Aacut -> Á - new Transition (8, 55), // &a -> &ae - new Transition (12, 13), // &aacut -> á - new Transition (16, 17), // &Abr -> &Abre - new Transition (18, 19), // &Abrev -> &Abreve - new Transition (22, 23), // &abr -> &abre - new Transition (24, 25), // &abrev -> &abreve - new Transition (43, 44), // &acut -> ´ - new Transition (70, 71), // &Agrav -> À - new Transition (76, 77), // &agrav -> à - new Transition (79, 80), // &al -> &ale - new Transition (131, 132), // &andslop -> &andslope - new Transition (136, 138), // &ang -> &ange - new Transition (140, 141), // &angl -> &angle - new Transition (147, 156), // &angmsda -> &angmsdae - new Transition (199, 208), // &ap -> &ape - new Transition (232, 234), // &approx -> &approxe - new Transition (264, 266), // &asymp -> &asympe - new Transition (272, 273), // &Atild -> à - new Transition (278, 279), // &atild -> ã - new Transition (301, 384), // &b -> &be - new Transition (304, 310), // &back -> &backe - new Transition (321, 322), // &backprim -> &backprime - new Transition (326, 328), // &backsim -> &backsime - new Transition (331, 390), // &B -> &Be - new Transition (345, 346), // &barv -> &barve - new Transition (346, 347), // &barve -> &barvee - new Transition (349, 350), // &Barw -> &Barwe - new Transition (353, 354), // &barw -> &barwe - new Transition (357, 358), // &barwedg -> &barwedge - new Transition (388, 397), // &becaus -> &because - new Transition (394, 395), // &Becaus -> &Because - new Transition (431, 432), // &betw -> &betwe - new Transition (432, 433), // &betwe -> &betwee - new Transition (467, 468), // &bigotim -> &bigotime - new Transition (487, 488), // &bigtriangl -> &bigtriangle - new Transition (503, 504), // &bigv -> &bigve - new Transition (504, 505), // &bigve -> &bigvee - new Transition (507, 508), // &bigw -> &bigwe - new Transition (510, 511), // &bigwedg -> &bigwedge - new Transition (525, 526), // &blackloz -> &blackloze - new Transition (528, 529), // &blacklozeng -> &blacklozenge - new Transition (535, 536), // &blacksquar -> &blacksquare - new Transition (544, 545), // &blacktriangl -> &blacktriangle - new Transition (552, 553), // &blacktrianglel -> &blacktrianglele - new Transition (579, 580), // &bn -> &bne - new Transition (610, 611), // &bowti -> &bowtie - new Transition (669, 670), // &boxtim -> &boxtime - new Transition (722, 723), // &bprim -> &bprime - new Transition (725, 726), // &Br -> &Bre - new Transition (727, 728), // &Brev -> &Breve - new Transition (730, 731), // &br -> &bre - new Transition (732, 733), // &brev -> &breve - new Transition (744, 748), // &bs -> &bse - new Transition (753, 755), // &bsim -> &bsime - new Transition (769, 771), // &bull -> &bulle - new Transition (775, 779), // &bump -> &bumpe - new Transition (783, 784), // &Bump -> &Bumpe - new Transition (789, 920), // &C -> &Ce - new Transition (793, 794), // &Cacut -> &Cacute - new Transition (796, 915), // &c -> &ce - new Transition (800, 801), // &cacut -> &cacute - new Transition (835, 836), // &CapitalDiff -> &CapitalDiffe - new Transition (837, 838), // &CapitalDiffer -> &CapitalDiffere - new Transition (848, 849), // &car -> &care - new Transition (856, 857), // &Cayl -> &Cayle - new Transition (861, 881), // &cc -> &cce - new Transition (866, 876), // &Cc -> &Cce - new Transition (934, 944), // ¢ -> ¢e - new Transition (937, 938), // &Cent -> &Cente - new Transition (960, 964), // &ch -> &che - new Transition (979, 1051), // &cir -> &cire - new Transition (981, 983), // &circ -> &circe - new Transition (986, 987), // &circl -> &circle - new Transition (993, 994), // &circlearrowl -> &circlearrowle - new Transition (1022, 1023), // &Circl -> &Circle - new Transition (1045, 1046), // &CircleTim -> &CircleTime - new Transition (1074, 1075), // &Clockwis -> &Clockwise - new Transition (1085, 1086), // &ClockwiseContourInt -> &ClockwiseContourInte - new Transition (1092, 1093), // &Clos -> &Close - new Transition (1103, 1104), // &CloseCurlyDoubl -> &CloseCurlyDouble - new Transition (1108, 1109), // &CloseCurlyDoubleQuot -> &CloseCurlyDoubleQuote - new Transition (1114, 1115), // &CloseCurlyQuot -> &CloseCurlyQuote - new Transition (1129, 1136), // &Colon -> &Colone - new Transition (1134, 1138), // &colon -> &colone - new Transition (1153, 1154), // &compl -> &comple - new Transition (1155, 1156), // &complem -> &compleme - new Transition (1160, 1161), // &complex -> &complexe - new Transition (1174, 1175), // &Congru -> &Congrue - new Transition (1193, 1194), // &ContourInt -> &ContourInte - new Transition (1228, 1229), // &Count -> &Counte - new Transition (1238, 1239), // &CounterClockwis -> &CounterClockwise - new Transition (1249, 1250), // &CounterClockwiseContourInt -> &CounterClockwiseContourInte - new Transition (1279, 1281), // &csub -> &csube - new Transition (1283, 1285), // &csup -> &csupe - new Transition (1292, 1301), // &cu -> &cue - new Transition (1354, 1355), // &curly -> &curlye - new Transition (1358, 1359), // &curlyeqpr -> &curlyeqpre - new Transition (1367, 1368), // &curlyv -> &curlyve - new Transition (1368, 1369), // &curlyve -> &curlyvee - new Transition (1371, 1372), // &curlyw -> &curlywe - new Transition (1374, 1375), // &curlywedg -> &curlywedge - new Transition (1377, 1378), // &curr -> &curre - new Transition (1381, 1382), // &curv -> &curve - new Transition (1388, 1389), // &curvearrowl -> &curvearrowle - new Transition (1399, 1400), // &cuv -> &cuve - new Transition (1400, 1401), // &cuve -> &cuvee - new Transition (1403, 1404), // &cuw -> &cuwe - new Transition (1425, 1519), // &D -> &De - new Transition (1428, 1429), // &Dagg -> &Dagge - new Transition (1432, 1516), // &d -> &de - new Transition (1435, 1436), // &dagg -> &dagge - new Transition (1439, 1440), // &dal -> &dale - new Transition (1496, 1497), // &ddagg -> &ddagge - new Transition (1512, 1513), // &ddots -> &ddotse - new Transition (1570, 1571), // &DiacriticalAcut -> &DiacriticalAcute - new Transition (1579, 1580), // &DiacriticalDoubl -> &DiacriticalDouble - new Transition (1584, 1585), // &DiacriticalDoubleAcut -> &DiacriticalDoubleAcute - new Transition (1590, 1591), // &DiacriticalGrav -> &DiacriticalGrave - new Transition (1596, 1597), // &DiacriticalTild -> &DiacriticalTilde - new Transition (1599, 1619), // &di -> &die - new Transition (1622, 1623), // &Diff -> &Diffe - new Transition (1624, 1625), // &Differ -> &Differe - new Transition (1646, 1647), // &divid -> ÷ - new Transition (1653, 1654), // ÷ontim -> ÷ontime - new Transition (1694, 1700), // &dot -> &dote - new Transition (1728, 1729), // &dotsquar -> &dotsquare - new Transition (1733, 1734), // &doubl -> &double - new Transition (1738, 1739), // &doublebarw -> &doublebarwe - new Transition (1741, 1742), // &doublebarwedg -> &doublebarwedge - new Transition (1746, 1747), // &Doubl -> &Double - new Transition (1757, 1758), // &DoubleContourInt -> &DoubleContourInte - new Transition (1776, 1777), // &DoubleL -> &DoubleLe - new Transition (1797, 1798), // &DoubleLeftT -> &DoubleLeftTe - new Transition (1798, 1799), // &DoubleLeftTe -> &DoubleLeftTee - new Transition (1804, 1805), // &DoubleLongL -> &DoubleLongLe - new Transition (1847, 1848), // &DoubleRightT -> &DoubleRightTe - new Transition (1848, 1849), // &DoubleRightTe -> &DoubleRightTee - new Transition (1869, 1870), // &DoubleV -> &DoubleVe - new Transition (1916, 1917), // &DownBr -> &DownBre - new Transition (1918, 1919), // &DownBrev -> &DownBreve - new Transition (1939, 1940), // &downharpoonl -> &downharpoonle - new Transition (1950, 1951), // &DownL -> &DownLe - new Transition (1959, 1960), // &DownLeftRightV -> &DownLeftRightVe - new Transition (1966, 1967), // &DownLeftT -> &DownLeftTe - new Transition (1967, 1968), // &DownLeftTe -> &DownLeftTee - new Transition (1969, 1970), // &DownLeftTeeV -> &DownLeftTeeVe - new Transition (1976, 1977), // &DownLeftV -> &DownLeftVe - new Transition (1992, 1993), // &DownRightT -> &DownRightTe - new Transition (1993, 1994), // &DownRightTe -> &DownRightTee - new Transition (1995, 1996), // &DownRightTeeV -> &DownRightTeeVe - new Transition (2002, 2003), // &DownRightV -> &DownRightVe - new Transition (2013, 2014), // &DownT -> &DownTe - new Transition (2014, 2015), // &DownTe -> &DownTee - new Transition (2090, 2091), // &dwangl -> &dwangle - new Transition (2112, 2113), // &Eacut -> É - new Transition (2115, 2173), // &e -> &ee - new Transition (2119, 2120), // &eacut -> é - new Transition (2123, 2124), // &east -> &easte - new Transition (2190, 2191), // &Egrav -> È - new Transition (2195, 2196), // &egrav -> è - new Transition (2206, 2207), // &El -> &Ele - new Transition (2208, 2209), // &Elem -> &Eleme - new Transition (2215, 2216), // &elint -> &elinte - new Transition (2242, 2243), // &emptys -> &emptyse - new Transition (2258, 2259), // &EmptySmallSquar -> &EmptySmallSquare - new Transition (2263, 2264), // &EmptyV -> &EmptyVe - new Transition (2276, 2277), // &EmptyVerySmallSquar -> &EmptyVerySmallSquare - new Transition (2362, 2363), // &eqslantl -> &eqslantle - new Transition (2372, 2383), // &equ -> &eque - new Transition (2380, 2381), // &EqualTild -> &EqualTilde - new Transition (2472, 2473), // &exp -> &expe - new Transition (2484, 2485), // &Expon -> &Expone - new Transition (2494, 2495), // &expon -> &expone - new Transition (2500, 2501), // &exponential -> &exponentiale - new Transition (2503, 2524), // &f -> &fe - new Transition (2513, 2514), // &fallingdots -> &fallingdotse - new Transition (2527, 2528), // &femal -> &female - new Transition (2556, 2557), // &Fill -> &Fille - new Transition (2568, 2569), // &FilledSmallSquar -> &FilledSmallSquare - new Transition (2571, 2572), // &FilledV -> &FilledVe - new Transition (2584, 2585), // &FilledVerySmallSquar -> &FilledVerySmallSquare - new Transition (2632, 2633), // &Fouri -> &Fourie - new Transition (2701, 2765), // &g -> &ge - new Transition (2705, 2706), // &gacut -> &gacute - new Transition (2725, 2726), // &Gbr -> &Gbre - new Transition (2727, 2728), // &Gbrev -> &Gbreve - new Transition (2731, 2732), // &gbr -> &gbre - new Transition (2733, 2734), // &gbrev -> &gbreve - new Transition (2736, 2737), // &Gc -> &Gce - new Transition (2794, 2796), // &gesl -> &gesle - new Transition (2812, 2813), // &gim -> &gime - new Transition (2832, 2843), // &gn -> &gne - new Transition (2863, 2864), // &grav -> &grave - new Transition (2866, 2867), // &Gr -> &Gre - new Transition (2869, 2870), // &Great -> &Greate - new Transition (2878, 2879), // &GreaterEqualL -> &GreaterEqualLe - new Transition (2894, 2895), // &GreaterGr -> &GreaterGre - new Transition (2897, 2898), // &GreaterGreat -> &GreaterGreate - new Transition (2901, 2902), // &GreaterL -> &GreaterLe - new Transition (2920, 2921), // &GreaterTild -> &GreaterTilde - new Transition (2932, 2934), // &gsim -> &gsime - new Transition (2960, 2961), // >qu -> >que - new Transition (2965, 2980), // >r -> >re - new Transition (2982, 2983), // >reql -> >reqle - new Transition (2988, 2989), // >reqql -> >reqqle - new Transition (2993, 2994), // >rl -> >rle - new Transition (3002, 3003), // &gv -> &gve - new Transition (3006, 3007), // &gvertn -> &gvertne - new Transition (3016, 3017), // &Hac -> &Hace - new Transition (3020, 3074), // &h -> &he - new Transition (3102, 3103), // &Hilb -> &Hilbe - new Transition (3109, 3110), // &HilbertSpac -> &HilbertSpace - new Transition (3113, 3114), // &hks -> &hkse - new Transition (3138, 3139), // &hookl -> &hookle - new Transition (3181, 3182), // &HorizontalLin -> &HorizontalLine - new Transition (3232, 3233), // &hyph -> &hyphe - new Transition (3240, 3241), // &Iacut -> Í - new Transition (3243, 3273), // &i -> &ie - new Transition (3247, 3248), // &iacut -> í - new Transition (3292, 3293), // &Igrav -> Ì - new Transition (3298, 3299), // &igrav -> ì - new Transition (3341, 3342), // &imag -> &image - new Transition (3354, 3355), // &imaglin -> &imagline - new Transition (3368, 3369), // &imp -> &impe - new Transition (3374, 3375), // &Impli -> &Implie - new Transition (3382, 3383), // &incar -> &incare - new Transition (3390, 3391), // &infinti -> &infintie - new Transition (3399, 3413), // &Int -> &Inte - new Transition (3401, 3407), // &int -> &inte - new Transition (3408, 3409), // &integ -> &intege - new Transition (3425, 3426), // &Inters -> &Interse - new Transition (3449, 3450), // &Invisibl -> &Invisible - new Transition (3459, 3460), // &InvisibleTim -> &InvisibleTime - new Transition (3498, 3499), // &iqu -> &ique - new Transition (3531, 3532), // &Itild -> &Itilde - new Transition (3536, 3537), // &itild -> &itilde - new Transition (3590, 3598), // &Js -> &Jse - new Transition (3594, 3603), // &js -> &jse - new Transition (3632, 3633), // &Kc -> &Kce - new Transition (3638, 3639), // &kc -> &kce - new Transition (3655, 3656), // &kgr -> &kgre - new Transition (3656, 3657), // &kgre -> &kgree - new Transition (3692, 3896), // &l -> &le - new Transition (3698, 3898), // &L -> &Le - new Transition (3702, 3703), // &Lacut -> &Lacute - new Transition (3705, 3711), // &la -> &lae - new Transition (3708, 3709), // &lacut -> &lacute - new Transition (3741, 3742), // &langl -> &langle - new Transition (3749, 3750), // &Laplac -> &Laplace - new Transition (3792, 3803), // &lat -> &late - new Transition (3823, 3824), // &lbrac -> &lbrace - new Transition (3828, 3829), // &lbrk -> &lbrke - new Transition (3837, 3849), // &Lc -> &Lce - new Transition (3843, 3854), // &lc -> &lce - new Transition (3904, 3905), // &LeftAngl -> &LeftAngle - new Transition (3910, 3911), // &LeftAngleBrack -> &LeftAngleBracke - new Transition (3953, 3954), // &LeftC -> &LeftCe - new Transition (3965, 3966), // &LeftDoubl -> &LeftDouble - new Transition (3971, 3972), // &LeftDoubleBrack -> &LeftDoubleBracke - new Transition (3977, 3978), // &LeftDownT -> &LeftDownTe - new Transition (3978, 3979), // &LeftDownTe -> &LeftDownTee - new Transition (3980, 3981), // &LeftDownTeeV -> &LeftDownTeeVe - new Transition (3987, 3988), // &LeftDownV -> &LeftDownVe - new Transition (4019, 4020), // &leftl -> &leftle - new Transition (4085, 4086), // &LeftRightV -> &LeftRightVe - new Transition (4092, 4093), // &LeftT -> &LeftTe - new Transition (4093, 4094), // &LeftTe -> &LeftTee - new Transition (4102, 4103), // &LeftTeeV -> &LeftTeeVe - new Transition (4111, 4112), // &leftthr -> &leftthre - new Transition (4112, 4113), // &leftthre -> &leftthree - new Transition (4116, 4117), // &leftthreetim -> &leftthreetime - new Transition (4125, 4126), // &LeftTriangl -> &LeftTriangle - new Transition (4144, 4145), // &LeftUpDownV -> &LeftUpDownVe - new Transition (4151, 4152), // &LeftUpT -> &LeftUpTe - new Transition (4152, 4153), // &LeftUpTe -> &LeftUpTee - new Transition (4154, 4155), // &LeftUpTeeV -> &LeftUpTeeVe - new Transition (4161, 4162), // &LeftUpV -> &LeftUpVe - new Transition (4172, 4173), // &LeftV -> &LeftVe - new Transition (4210, 4212), // &lesg -> &lesge - new Transition (4215, 4227), // &less -> &lesse - new Transition (4246, 4247), // &LessEqualGr -> &LessEqualGre - new Transition (4249, 4250), // &LessEqualGreat -> &LessEqualGreate - new Transition (4264, 4265), // &LessGr -> &LessGre - new Transition (4267, 4268), // &LessGreat -> &LessGreate - new Transition (4275, 4276), // &LessL -> &LessLe - new Transition (4298, 4299), // &LessTild -> &LessTilde - new Transition (4346, 4361), // &Ll -> &Lle - new Transition (4357, 4358), // &llcorn -> &llcorne - new Transition (4398, 4399), // &lmoustach -> &lmoustache - new Transition (4401, 4412), // &ln -> &lne - new Transition (4437, 4438), // &LongL -> &LongLe - new Transition (4447, 4448), // &Longl -> &Longle - new Transition (4459, 4460), // &longl -> &longle - new Transition (4549, 4550), // &looparrowl -> &looparrowle - new Transition (4575, 4576), // &lotim -> &lotime - new Transition (4588, 4589), // &Low -> &Lowe - new Transition (4591, 4592), // &LowerL -> &LowerLe - new Transition (4612, 4614), // &loz -> &loze - new Transition (4616, 4617), // &lozeng -> &lozenge - new Transition (4636, 4637), // &lrcorn -> &lrcorne - new Transition (4670, 4672), // &lsim -> &lsime - new Transition (4711, 4712), // <hr -> <hre - new Transition (4712, 4713), // <hre -> <hree - new Transition (4716, 4717), // <im -> <ime - new Transition (4726, 4727), // <qu -> <que - new Transition (4732, 4734), // <ri -> <rie - new Transition (4755, 4756), // &lv -> &lve - new Transition (4759, 4760), // &lvertn -> &lvertne - new Transition (4767, 4830), // &m -> &me - new Transition (4772, 4773), // &mal -> &male - new Transition (4775, 4777), // &malt -> &malte - new Transition (4778, 4779), // &maltes -> &maltese - new Transition (4781, 4843), // &M -> &Me - new Transition (4796, 4797), // &mapstol -> &mapstole - new Transition (4805, 4806), // &mark -> &marke - new Transition (4834, 4835), // &measur -> &measure - new Transition (4840, 4841), // &measuredangl -> &measuredangle - new Transition (4851, 4852), // &MediumSpac -> &MediumSpace - new Transition (4923, 4924), // &mod -> &mode - new Transition (4965, 5064), // &n -> &ne - new Transition (4971, 5084), // &N -> &Ne - new Transition (4975, 4976), // &Nacut -> &Nacute - new Transition (4980, 4981), // &nacut -> &nacute - new Transition (5016, 5018), // &nbump -> &nbumpe - new Transition (5020, 5039), // &nc -> &nce - new Transition (5024, 5034), // &Nc -> &Nce - new Transition (5089, 5090), // &Negativ -> &Negative - new Transition (5091, 5092), // &NegativeM -> &NegativeMe - new Transition (5100, 5101), // &NegativeMediumSpac -> &NegativeMediumSpace - new Transition (5111, 5112), // &NegativeThickSpac -> &NegativeThickSpace - new Transition (5118, 5119), // &NegativeThinSpac -> &NegativeThinSpace - new Transition (5121, 5122), // &NegativeV -> &NegativeVe - new Transition (5132, 5133), // &NegativeVeryThinSpac -> &NegativeVeryThinSpace - new Transition (5140, 5141), // &nes -> &nese - new Transition (5149, 5150), // &Nest -> &Neste - new Transition (5153, 5154), // &NestedGr -> &NestedGre - new Transition (5156, 5157), // &NestedGreat -> &NestedGreate - new Transition (5160, 5161), // &NestedGreaterGr -> &NestedGreaterGre - new Transition (5163, 5164), // &NestedGreaterGreat -> &NestedGreaterGreate - new Transition (5167, 5168), // &NestedL -> &NestedLe - new Transition (5171, 5172), // &NestedLessL -> &NestedLessLe - new Transition (5179, 5180), // &NewLin -> &NewLine - new Transition (5195, 5198), // &ng -> &nge - new Transition (5256, 5270), // &nl -> &nle - new Transition (5272, 5273), // &nL -> &nLe - new Transition (5337, 5339), // &nltri -> &nltrie - new Transition (5349, 5350), // &NoBr -> &NoBre - new Transition (5356, 5357), // &NonBr -> &NonBre - new Transition (5366, 5367), // &NonBreakingSpac -> &NonBreakingSpace - new Transition (5385, 5386), // &NotCongru -> &NotCongrue - new Transition (5400, 5401), // &NotDoubl -> &NotDouble - new Transition (5402, 5403), // &NotDoubleV -> &NotDoubleVe - new Transition (5415, 5416), // &NotEl -> &NotEle - new Transition (5417, 5418), // &NotElem -> &NotEleme - new Transition (5430, 5431), // &NotEqualTild -> &NotEqualTilde - new Transition (5440, 5441), // &NotGr -> &NotGre - new Transition (5443, 5444), // &NotGreat -> &NotGreate - new Transition (5464, 5465), // &NotGreaterGr -> &NotGreaterGre - new Transition (5467, 5468), // &NotGreaterGreat -> &NotGreaterGreate - new Transition (5471, 5472), // &NotGreaterL -> &NotGreaterLe - new Transition (5490, 5491), // &NotGreaterTild -> &NotGreaterTilde - new Transition (5528, 5529), // &NotL -> &NotLe - new Transition (5538, 5539), // &NotLeftTriangl -> &NotLeftTriangle - new Transition (5561, 5562), // &NotLessGr -> &NotLessGre - new Transition (5564, 5565), // &NotLessGreat -> &NotLessGreate - new Transition (5568, 5569), // &NotLessL -> &NotLessLe - new Transition (5587, 5588), // &NotLessTild -> &NotLessTilde - new Transition (5590, 5591), // &NotN -> &NotNe - new Transition (5593, 5594), // &NotNest -> &NotNeste - new Transition (5597, 5598), // &NotNestedGr -> &NotNestedGre - new Transition (5600, 5601), // &NotNestedGreat -> &NotNestedGreate - new Transition (5604, 5605), // &NotNestedGreaterGr -> &NotNestedGreaterGre - new Transition (5607, 5608), // &NotNestedGreaterGreat -> &NotNestedGreaterGreate - new Transition (5611, 5612), // &NotNestedL -> &NotNestedLe - new Transition (5615, 5616), // &NotNestedLessL -> &NotNestedLessLe - new Transition (5631, 5632), // &NotPr -> &NotPre - new Transition (5633, 5634), // &NotPrec -> &NotPrece - new Transition (5635, 5636), // &NotPreced -> &NotPrecede - new Transition (5656, 5657), // &NotR -> &NotRe - new Transition (5658, 5659), // &NotRev -> &NotReve - new Transition (5661, 5662), // &NotRevers -> &NotReverse - new Transition (5664, 5665), // &NotReverseEl -> &NotReverseEle - new Transition (5666, 5667), // &NotReverseElem -> &NotReverseEleme - new Transition (5681, 5682), // &NotRightTriangl -> &NotRightTriangle - new Transition (5698, 5699), // &NotSquar -> &NotSquare - new Transition (5703, 5704), // &NotSquareSubs -> &NotSquareSubse - new Transition (5713, 5714), // &NotSquareSup -> &NotSquareSupe - new Transition (5716, 5717), // &NotSquareSupers -> &NotSquareSuperse - new Transition (5728, 5729), // &NotSubs -> &NotSubse - new Transition (5739, 5740), // &NotSucc -> &NotSucce - new Transition (5740, 5741), // &NotSucce -> &NotSuccee - new Transition (5765, 5766), // &NotSucceedsTild -> &NotSucceedsTilde - new Transition (5768, 5769), // &NotSup -> &NotSupe - new Transition (5771, 5772), // &NotSupers -> &NotSuperse - new Transition (5784, 5785), // &NotTild -> &NotTilde - new Transition (5806, 5807), // &NotTildeTild -> &NotTildeTilde - new Transition (5809, 5810), // &NotV -> &NotVe - new Transition (5827, 5828), // &nparall -> &nparalle - new Transition (5842, 5848), // &npr -> &npre - new Transition (5845, 5846), // &nprcu -> &nprcue - new Transition (5850, 5852), // &nprec -> &nprece - new Transition (5891, 5893), // &nrtri -> &nrtrie - new Transition (5896, 5902), // &nsc -> &nsce - new Transition (5899, 5900), // &nsccu -> &nsccue - new Transition (5923, 5924), // &nshortparall -> &nshortparalle - new Transition (5928, 5930), // &nsim -> &nsime - new Transition (5945, 5946), // &nsqsub -> &nsqsube - new Transition (5948, 5949), // &nsqsup -> &nsqsupe - new Transition (5952, 5956), // &nsub -> &nsube - new Transition (5958, 5959), // &nsubs -> &nsubse - new Transition (5960, 5962), // &nsubset -> &nsubsete - new Transition (5968, 5970), // &nsucc -> &nsucce - new Transition (5973, 5977), // &nsup -> &nsupe - new Transition (5979, 5980), // &nsups -> &nsupse - new Transition (5981, 5983), // &nsupset -> &nsupsete - new Transition (5995, 5996), // &Ntild -> Ñ - new Transition (6000, 6001), // &ntild -> ñ - new Transition (6011, 6012), // &ntriangl -> &ntriangle - new Transition (6013, 6014), // &ntrianglel -> &ntrianglele - new Transition (6016, 6018), // &ntriangleleft -> &ntrianglelefte - new Transition (6025, 6027), // &ntriangleright -> &ntrianglerighte - new Transition (6034, 6036), // &num -> &nume - new Transition (6068, 6069), // &nvg -> &nvge - new Transition (6084, 6089), // &nvl -> &nvle - new Transition (6094, 6095), // &nvltri -> &nvltrie - new Transition (6104, 6105), // &nvrtri -> &nvrtrie - new Transition (6126, 6127), // &nwn -> &nwne - new Transition (6135, 6136), // &Oacut -> Ó - new Transition (6138, 6195), // &o -> &oe - new Transition (6142, 6143), // &oacut -> ó - new Transition (6217, 6218), // &Ograv -> Ò - new Transition (6222, 6223), // &ograv -> ò - new Transition (6253, 6254), // &olin -> &oline - new Transition (6258, 6268), // &Om -> &Ome - new Transition (6263, 6272), // &om -> &ome - new Transition (6302, 6332), // &op -> &ope - new Transition (6306, 6307), // &Op -> &Ope - new Transition (6318, 6319), // &OpenCurlyDoubl -> &OpenCurlyDouble - new Transition (6323, 6324), // &OpenCurlyDoubleQuot -> &OpenCurlyDoubleQuote - new Transition (6329, 6330), // &OpenCurlyQuot -> &OpenCurlyQuote - new Transition (6348, 6350), // &ord -> &orde - new Transition (6371, 6372), // &orslop -> &orslope - new Transition (6402, 6403), // &Otild -> Õ - new Transition (6408, 6409), // &otild -> õ - new Transition (6411, 6412), // &Otim -> &Otime - new Transition (6415, 6416), // &otim -> &otime - new Transition (6435, 6436), // &Ov -> &Ove - new Transition (6444, 6445), // &OverBrac -> &OverBrace - new Transition (6447, 6448), // &OverBrack -> &OverBracke - new Transition (6453, 6454), // &OverPar -> &OverPare - new Transition (6457, 6458), // &OverParenth -> &OverParenthe - new Transition (6463, 6497), // &p -> &pe - new Transition (6470, 6471), // ¶ll -> ¶lle - new Transition (6513, 6514), // &pert -> &perte - new Transition (6538, 6539), // &phon -> &phone - new Transition (6567, 6585), // &plus -> &pluse - new Transition (6614, 6615), // &Poincar -> &Poincare - new Transition (6619, 6620), // &Poincareplan -> &Poincareplane - new Transition (6640, 6672), // &Pr -> &Pre - new Transition (6642, 6653), // &pr -> &pre - new Transition (6648, 6649), // &prcu -> &prcue - new Transition (6655, 6702), // &prec -> &prece - new Transition (6668, 6669), // &preccurly -> &preccurlye - new Transition (6673, 6674), // &Prec -> &Prece - new Transition (6675, 6676), // &Preced -> &Precede - new Transition (6699, 6700), // &PrecedesTild -> &PrecedesTilde - new Transition (6705, 6713), // &precn -> &precne - new Transition (6726, 6727), // &Prim -> &Prime - new Transition (6730, 6731), // &prim -> &prime - new Transition (6762, 6763), // &proflin -> &profline - new Transition (6791, 6792), // &prur -> &prure - new Transition (6836, 6837), // &qprim -> &qprime - new Transition (6847, 6862), // &qu -> &que - new Transition (6849, 6850), // &quat -> &quate - new Transition (6864, 6866), // &quest -> &queste - new Transition (6876, 7074), // &r -> &re - new Transition (6882, 6901), // &ra -> &rae - new Transition (6883, 6884), // &rac -> &race - new Transition (6886, 7072), // &R -> &Re - new Transition (6890, 6891), // &Racut -> &Racute - new Transition (6894, 6895), // &racut -> &racute - new Transition (6912, 6916), // &rang -> &range - new Transition (6918, 6919), // &rangl -> &rangle - new Transition (7007, 7008), // &rbrac -> &rbrace - new Transition (7012, 7013), // &rbrk -> &rbrke - new Transition (7021, 7033), // &Rc -> &Rce - new Transition (7027, 7038), // &rc -> &rce - new Transition (7079, 7080), // &realin -> &realine - new Transition (7097, 7098), // &Rev -> &Reve - new Transition (7100, 7101), // &Revers -> &Reverse - new Transition (7103, 7104), // &ReverseEl -> &ReverseEle - new Transition (7105, 7106), // &ReverseElem -> &ReverseEleme - new Transition (7178, 7179), // &RightAngl -> &RightAngle - new Transition (7184, 7185), // &RightAngleBrack -> &RightAngleBracke - new Transition (7213, 7214), // &RightArrowL -> &RightArrowLe - new Transition (7228, 7229), // &RightC -> &RightCe - new Transition (7240, 7241), // &RightDoubl -> &RightDouble - new Transition (7246, 7247), // &RightDoubleBrack -> &RightDoubleBracke - new Transition (7252, 7253), // &RightDownT -> &RightDownTe - new Transition (7253, 7254), // &RightDownTe -> &RightDownTee - new Transition (7255, 7256), // &RightDownTeeV -> &RightDownTeeVe - new Transition (7262, 7263), // &RightDownV -> &RightDownVe - new Transition (7294, 7295), // &rightl -> &rightle - new Transition (7337, 7338), // &RightT -> &RightTe - new Transition (7338, 7339), // &RightTe -> &RightTee - new Transition (7347, 7348), // &RightTeeV -> &RightTeeVe - new Transition (7356, 7357), // &rightthr -> &rightthre - new Transition (7357, 7358), // &rightthre -> &rightthree - new Transition (7361, 7362), // &rightthreetim -> &rightthreetime - new Transition (7370, 7371), // &RightTriangl -> &RightTriangle - new Transition (7389, 7390), // &RightUpDownV -> &RightUpDownVe - new Transition (7396, 7397), // &RightUpT -> &RightUpTe - new Transition (7397, 7398), // &RightUpTe -> &RightUpTee - new Transition (7399, 7400), // &RightUpTeeV -> &RightUpTeeVe - new Transition (7406, 7407), // &RightUpV -> &RightUpVe - new Transition (7417, 7418), // &RightV -> &RightVe - new Transition (7438, 7439), // &risingdots -> &risingdotse - new Transition (7461, 7462), // &rmoustach -> &rmoustache - new Transition (7497, 7498), // &rotim -> &rotime - new Transition (7508, 7509), // &RoundImpli -> &RoundImplie - new Transition (7569, 7570), // &rthr -> &rthre - new Transition (7570, 7571), // &rthre -> &rthree - new Transition (7574, 7575), // &rtim -> &rtime - new Transition (7579, 7581), // &rtri -> &rtrie - new Transition (7591, 7592), // &Rul -> &Rule - new Transition (7593, 7594), // &RuleD -> &RuleDe - new Transition (7597, 7598), // &RuleDelay -> &RuleDelaye - new Transition (7614, 7615), // &Sacut -> &Sacute - new Transition (7617, 7703), // &s -> &se - new Transition (7621, 7622), // &sacut -> &sacute - new Transition (7629, 7653), // &Sc -> &Sce - new Transition (7631, 7651), // &sc -> &sce - new Transition (7646, 7647), // &sccu -> &sccue - new Transition (7697, 7701), // &sdot -> &sdote - new Transition (7786, 7787), // &ShortL -> &ShortLe - new Transition (7808, 7809), // &shortparall -> &shortparalle - new Transition (7847, 7853), // &sim -> &sime - new Transition (7865, 7866), // &simn -> &simne - new Transition (7891, 7892), // &SmallCircl -> &SmallCircle - new Transition (7894, 7911), // &sm -> &sme - new Transition (7898, 7899), // &smalls -> &smallse - new Transition (7921, 7922), // &smil -> &smile - new Transition (7924, 7926), // &smt -> &smte - new Transition (7958, 7959), // &spad -> &spade - new Transition (7986, 7988), // &sqsub -> &sqsube - new Transition (7990, 7991), // &sqsubs -> &sqsubse - new Transition (7992, 7994), // &sqsubset -> &sqsubsete - new Transition (7997, 7999), // &sqsup -> &sqsupe - new Transition (8001, 8002), // &sqsups -> &sqsupse - new Transition (8003, 8005), // &sqsupset -> &sqsupsete - new Transition (8012, 8013), // &Squar -> &Square - new Transition (8016, 8017), // &squar -> &square - new Transition (8021, 8022), // &SquareInt -> &SquareInte - new Transition (8024, 8025), // &SquareInters -> &SquareInterse - new Transition (8035, 8036), // &SquareSubs -> &SquareSubse - new Transition (8045, 8046), // &SquareSup -> &SquareSupe - new Transition (8048, 8049), // &SquareSupers -> &SquareSuperse - new Transition (8077, 8081), // &ss -> &sse - new Transition (8088, 8089), // &ssmil -> &ssmile - new Transition (8111, 8112), // &straight -> &straighte - new Transition (8131, 8139), // &sub -> &sube - new Transition (8150, 8153), // &subn -> &subne - new Transition (8165, 8166), // &Subs -> &Subse - new Transition (8169, 8170), // &subs -> &subse - new Transition (8171, 8173), // &subset -> &subsete - new Transition (8184, 8185), // &subsetn -> &subsetne - new Transition (8199, 8246), // &succ -> &succe - new Transition (8212, 8213), // &succcurly -> &succcurlye - new Transition (8217, 8218), // &Succ -> &Succe - new Transition (8218, 8219), // &Succe -> &Succee - new Transition (8243, 8244), // &SucceedsTild -> &SucceedsTilde - new Transition (8249, 8257), // &succn -> &succne - new Transition (8282, 8308), // &Sup -> &Supe - new Transition (8284, 8302), // &sup -> &supe - new Transition (8310, 8311), // &Supers -> &Superse - new Transition (8338, 8341), // &supn -> &supne - new Transition (8348, 8349), // &Sups -> &Supse - new Transition (8352, 8353), // &sups -> &supse - new Transition (8354, 8356), // &supset -> &supsete - new Transition (8361, 8362), // &supsetn -> &supsetne - new Transition (8404, 8449), // &t -> &te - new Transition (8407, 8408), // &targ -> &targe - new Transition (8419, 8431), // &Tc -> &Tce - new Transition (8425, 8436), // &tc -> &tce - new Transition (8451, 8452), // &telr -> &telre - new Transition (8461, 8462), // &th -> &the - new Transition (8463, 8464), // &ther -> &there - new Transition (8467, 8468), // &Th -> &The - new Transition (8469, 8470), // &Ther -> &There - new Transition (8473, 8474), // &Therefor -> &Therefore - new Transition (8478, 8479), // &therefor -> &therefore - new Transition (8513, 8514), // &ThickSpac -> &ThickSpace - new Transition (8524, 8525), // &ThinSpac -> &ThinSpace - new Transition (8546, 8547), // &Tild -> &Tilde - new Transition (8551, 8552), // &tild -> &tilde - new Transition (8573, 8574), // &TildeTild -> &TildeTilde - new Transition (8576, 8577), // &tim -> &time - new Transition (8590, 8591), // &to -> &toe - new Transition (8620, 8621), // &tprim -> &tprime - new Transition (8630, 8631), // &trad -> &trade - new Transition (8633, 8668), // &tri -> &trie - new Transition (8637, 8638), // &triangl -> &triangle - new Transition (8645, 8646), // &trianglel -> &trianglele - new Transition (8648, 8650), // &triangleleft -> &trianglelefte - new Transition (8659, 8661), // &triangleright -> &trianglerighte - new Transition (8679, 8680), // &Tripl -> &Triple - new Transition (8695, 8696), // &tritim -> &tritime - new Transition (8698, 8699), // &trp -> &trpe - new Transition (8743, 8744), // &twoh -> &twohe - new Transition (8747, 8748), // &twoheadl -> &twoheadle - new Transition (8772, 8773), // &Uacut -> Ú - new Transition (8779, 8780), // &uacut -> ú - new Transition (8798, 8807), // &Ubr -> &Ubre - new Transition (8803, 8811), // &ubr -> &ubre - new Transition (8808, 8809), // &Ubrev -> &Ubreve - new Transition (8812, 8813), // &ubrev -> &ubreve - new Transition (8863, 8864), // &Ugrav -> Ù - new Transition (8869, 8870), // &ugrav -> ù - new Transition (8891, 8893), // &ulcorn -> &ulcorne - new Transition (8917, 8918), // &Und -> &Unde - new Transition (8926, 8927), // &UnderBrac -> &UnderBrace - new Transition (8929, 8930), // &UnderBrack -> &UnderBracke - new Transition (8935, 8936), // &UnderPar -> &UnderPare - new Transition (8939, 8940), // &UnderParenth -> &UnderParenthe - new Transition (9053, 9054), // &upharpoonl -> &upharpoonle - new Transition (9068, 9069), // &Upp -> &Uppe - new Transition (9071, 9072), // &UpperL -> &UpperLe - new Transition (9108, 9109), // &UpT -> &UpTe - new Transition (9109, 9110), // &UpTe -> &UpTee - new Transition (9131, 9133), // &urcorn -> &urcorne - new Transition (9169, 9170), // &Utild -> &Utilde - new Transition (9174, 9175), // &utild -> &utilde - new Transition (9198, 9199), // &uwangl -> &uwangle - new Transition (9201, 9345), // &v -> &ve - new Transition (9208, 9209), // &var -> &vare - new Transition (9260, 9261), // &varsubs -> &varsubse - new Transition (9263, 9264), // &varsubsetn -> &varsubsetne - new Transition (9270, 9271), // &varsups -> &varsupse - new Transition (9273, 9274), // &varsupsetn -> &varsupsetne - new Transition (9280, 9281), // &varth -> &varthe - new Transition (9290, 9291), // &vartriangl -> &vartriangle - new Transition (9292, 9293), // &vartrianglel -> &vartrianglele - new Transition (9303, 9342), // &V -> &Ve - new Transition (9342, 9343), // &Ve -> &Vee - new Transition (9345, 9346), // &ve -> &vee - new Transition (9346, 9352), // &vee -> &veee - new Transition (9384, 9385), // &VerticalLin -> &VerticalLine - new Transition (9387, 9388), // &VerticalS -> &VerticalSe - new Transition (9400, 9401), // &VerticalTild -> &VerticalTilde - new Transition (9411, 9412), // &VeryThinSpac -> &VeryThinSpace - new Transition (9460, 9463), // &vsubn -> &vsubne - new Transition (9466, 9469), // &vsupn -> &vsupne - new Transition (9484, 9502), // &W -> &We - new Transition (9490, 9496), // &w -> &we - new Transition (9504, 9505), // &Wedg -> &Wedge - new Transition (9507, 9508), // &wedg -> &wedge - new Transition (9512, 9513), // &wei -> &weie - new Transition (9533, 9535), // &wr -> &wre - new Transition (9620, 9621), // &xotim -> &xotime - new Transition (9655, 9656), // &xv -> &xve - new Transition (9656, 9657), // &xve -> &xvee - new Transition (9659, 9660), // &xw -> &xwe - new Transition (9662, 9663), // &xwedg -> &xwedge - new Transition (9669, 9670), // &Yacut -> Ý - new Transition (9672, 9699), // &y -> &ye - new Transition (9676, 9677), // &yacut -> ý - new Transition (9747, 9791), // &Z -> &Ze - new Transition (9751, 9752), // &Zacut -> &Zacute - new Transition (9754, 9785), // &z -> &ze - new Transition (9758, 9759), // &zacut -> &zacute - new Transition (9785, 9786), // &ze -> &zee - new Transition (9802, 9803) // &ZeroWidthSpac -> &ZeroWidthSpace - }; - TransitionTable_f = new Transition[177] { - new Transition (0, 2503), // & -> &f - new Transition (1, 62), // &A -> &Af - new Transition (8, 60), // &a -> &af - new Transition (80, 81), // &ale -> &alef - new Transition (147, 158), // &angmsda -> &angmsdaf - new Transition (193, 194), // &Aop -> &Aopf - new Transition (196, 197), // &aop -> &aopf - new Transition (301, 439), // &b -> &bf - new Transition (331, 436), // &B -> &Bf - new Transition (553, 554), // &blacktrianglele -> &blacktrianglelef - new Transition (595, 596), // &Bop -> &Bopf - new Transition (599, 600), // &bop -> &bopf - new Transition (789, 950), // &C -> &Cf - new Transition (796, 953), // &c -> &cf - new Transition (833, 834), // &CapitalDi -> &CapitalDif - new Transition (834, 835), // &CapitalDif -> &CapitalDiff - new Transition (979, 1053), // &cir -> &cirf - new Transition (994, 995), // &circlearrowle -> &circlearrowlef - new Transition (1148, 1150), // &comp -> &compf - new Transition (1200, 1201), // &Cop -> &Copf - new Transition (1203, 1204), // &cop -> &copf - new Transition (1389, 1390), // &curvearrowle -> &curvearrowlef - new Transition (1425, 1541), // &D -> &Df - new Transition (1432, 1535), // &d -> &df - new Transition (1557, 1621), // &Di -> &Dif - new Transition (1621, 1622), // &Dif -> &Diff - new Transition (1686, 1687), // &Dop -> &Dopf - new Transition (1689, 1690), // &dop -> &dopf - new Transition (1777, 1778), // &DoubleLe -> &DoubleLef - new Transition (1805, 1806), // &DoubleLongLe -> &DoubleLongLef - new Transition (1940, 1941), // &downharpoonle -> &downharpoonlef - new Transition (1951, 1952), // &DownLe -> &DownLef - new Transition (2073, 2075), // &dtri -> &dtrif - new Transition (2108, 2180), // &E -> &Ef - new Transition (2115, 2175), // &e -> &ef - new Transition (2306, 2307), // &Eop -> &Eopf - new Transition (2309, 2310), // &eop -> &eopf - new Transition (2503, 2530), // &f -> &ff - new Transition (2517, 2544), // &F -> &Ff - new Transition (2605, 2606), // &fno -> &fnof - new Transition (2609, 2610), // &Fop -> &Fopf - new Transition (2613, 2614), // &fop -> &fopf - new Transition (2636, 2637), // &Fouriertr -> &Fouriertrf - new Transition (2701, 2802), // &g -> &gf - new Transition (2708, 2799), // &G -> &Gf - new Transition (2854, 2855), // &Gop -> &Gopf - new Transition (2858, 2859), // &gop -> &gopf - new Transition (3014, 3094), // &H -> &Hf - new Transition (3020, 3097), // &h -> &hf - new Transition (3027, 3028), // &hal -> &half - new Transition (3139, 3140), // &hookle -> &hooklef - new Transition (3160, 3161), // &Hop -> &Hopf - new Transition (3163, 3164), // &hop -> &hopf - new Transition (3236, 3284), // &I -> &If - new Transition (3243, 3281), // &i -> &if - new Transition (3281, 3282), // &if -> &iff - new Transition (3311, 3312), // &iin -> &iinf - new Transition (3365, 3366), // &imo -> &imof - new Transition (3378, 3385), // &in -> &inf - new Transition (3480, 3481), // &Iop -> &Iopf - new Transition (3483, 3484), // &iop -> &iopf - new Transition (3555, 3571), // &J -> &Jf - new Transition (3561, 3574), // &j -> &jf - new Transition (3583, 3584), // &Jop -> &Jopf - new Transition (3587, 3588), // &jop -> &jopf - new Transition (3618, 3648), // &K -> &Kf - new Transition (3624, 3651), // &k -> &kf - new Transition (3677, 3678), // &Kop -> &Kopf - new Transition (3681, 3682), // &kop -> &kopf - new Transition (3692, 4301), // &l -> &lf - new Transition (3698, 4312), // &L -> &Lf - new Transition (3752, 3753), // &Laplacetr -> &Laplacetrf - new Transition (3766, 3773), // &larr -> &larrf - new Transition (3768, 3770), // &larrb -> &larrbf - new Transition (3896, 3925), // &le -> &lef - new Transition (3898, 3899), // &Le -> &Lef - new Transition (4020, 4021), // &leftle -> &leftlef - new Transition (4361, 4362), // &Lle -> &Llef - new Transition (4438, 4439), // &LongLe -> &LongLef - new Transition (4448, 4449), // &Longle -> &Longlef - new Transition (4460, 4461), // &longle -> &longlef - new Transition (4550, 4551), // &looparrowle -> &looparrowlef - new Transition (4560, 4567), // &lop -> &lopf - new Transition (4564, 4565), // &Lop -> &Lopf - new Transition (4592, 4593), // &LowerLe -> &LowerLef - new Transition (4612, 4619), // &loz -> &lozf - new Transition (4732, 4736), // <ri -> <rif - new Transition (4767, 4865), // &m -> &mf - new Transition (4781, 4862), // &M -> &Mf - new Transition (4797, 4798), // &mapstole -> &mapstolef - new Transition (4859, 4860), // &Mellintr -> &Mellintrf - new Transition (4929, 4930), // &Mop -> &Mopf - new Transition (4932, 4933), // &mop -> &mopf - new Transition (4965, 5192), // &n -> &nf - new Transition (4971, 5189), // &N -> &Nf - new Transition (5270, 5282), // &nle -> &nlef - new Transition (5273, 5274), // &nLe -> &nLef - new Transition (5369, 5370), // &Nop -> &Nopf - new Transition (5373, 5374), // &nop -> &nopf - new Transition (5529, 5530), // &NotLe -> &NotLef - new Transition (6014, 6015), // &ntrianglele -> &ntrianglelef - new Transition (6079, 6080), // &nvin -> &nvinf - new Transition (6131, 6205), // &O -> &Of - new Transition (6138, 6200), // &o -> &of - new Transition (6295, 6296), // &Oop -> &Oopf - new Transition (6299, 6300), // &oop -> &oopf - new Transition (6348, 6356), // &ord -> ª - new Transition (6353, 6354), // &ordero -> &orderof - new Transition (6362, 6363), // &origo -> &origof - new Transition (6463, 6521), // &p -> &pf - new Transition (6482, 6518), // &P -> &Pf - new Transition (6547, 6548), // &pitch -> &pitchf - new Transition (6630, 6631), // &Pop -> &Popf - new Transition (6633, 6634), // &pop -> &popf - new Transition (6745, 6754), // &pro -> &prof - new Transition (6767, 6768), // &profsur -> &profsurf - new Transition (6813, 6814), // &Q -> &Qf - new Transition (6817, 6818), // &q -> &qf - new Transition (6826, 6827), // &Qop -> &Qopf - new Transition (6830, 6831), // &qop -> &qopf - new Transition (6876, 7135), // &r -> &rf - new Transition (6886, 7146), // &R -> &Rf - new Transition (6932, 6944), // &rarr -> &rarrf - new Transition (6937, 6939), // &rarrb -> &rarrbf - new Transition (7214, 7215), // &RightArrowLe -> &RightArrowLef - new Transition (7295, 7296), // &rightle -> &rightlef - new Transition (7481, 7489), // &rop -> &ropf - new Transition (7486, 7487), // &Rop -> &Ropf - new Transition (7579, 7583), // &rtri -> &rtrif - new Transition (7610, 7741), // &S -> &Sf - new Transition (7617, 7744), // &s -> &sf - new Transition (7787, 7788), // &ShortLe -> &ShortLef - new Transition (7841, 7843), // &sigma -> &sigmaf - new Transition (7936, 7937), // &so -> &sof - new Transition (7950, 7951), // &Sop -> &Sopf - new Transition (7953, 7954), // &sop -> &sopf - new Transition (8008, 8066), // &squ -> &squf - new Transition (8016, 8064), // &squar -> &squarf - new Transition (8093, 8094), // &sstar -> &sstarf - new Transition (8102, 8104), // &star -> &starf - new Transition (8400, 8455), // &T -> &Tf - new Transition (8404, 8458), // &t -> &tf - new Transition (8464, 8476), // &there -> &theref - new Transition (8470, 8471), // &There -> &Theref - new Transition (8594, 8608), // &top -> &topf - new Transition (8605, 8606), // &Top -> &Topf - new Transition (8646, 8647), // &trianglele -> &trianglelef - new Transition (8748, 8749), // &twoheadle -> &twoheadlef - new Transition (8768, 8855), // &U -> &Uf - new Transition (8775, 8849), // &u -> &uf - new Transition (8964, 8965), // &Uop -> &Uopf - new Transition (8967, 8968), // &uop -> &uopf - new Transition (9054, 9055), // &upharpoonle -> &upharpoonlef - new Transition (9072, 9073), // &UpperLe -> &UpperLef - new Transition (9178, 9180), // &utri -> &utrif - new Transition (9201, 9417), // &v -> &vf - new Transition (9293, 9294), // &vartrianglele -> &vartrianglelef - new Transition (9303, 9414), // &V -> &Vf - new Transition (9433, 9434), // &Vop -> &Vopf - new Transition (9437, 9438), // &vop -> &vopf - new Transition (9484, 9517), // &W -> &Wf - new Transition (9490, 9520), // &w -> &wf - new Transition (9524, 9525), // &Wop -> &Wopf - new Transition (9528, 9529), // &wop -> &wopf - new Transition (9548, 9569), // &x -> &xf - new Transition (9565, 9566), // &X -> &Xf - new Transition (9608, 9609), // &Xop -> &Xopf - new Transition (9611, 9612), // &xop -> &xopf - new Transition (9665, 9702), // &Y -> &Yf - new Transition (9672, 9705), // &y -> &yf - new Transition (9717, 9718), // &Yop -> &Yopf - new Transition (9721, 9722), // &yop -> &yopf - new Transition (9747, 9811), // &Z -> &Zf - new Transition (9754, 9814), // &z -> &zf - new Transition (9788, 9789), // &zeetr -> &zeetrf - new Transition (9833, 9834), // &Zop -> &Zopf - new Transition (9837, 9838) // &zop -> &zopf - }; - TransitionTable_g = new Transition[182] { - new Transition (0, 2701), // & -> &g - new Transition (1, 67), // &A -> &Ag - new Transition (8, 73), // &a -> &ag - new Transition (52, 53), // &AEli -> Æ - new Transition (57, 58), // &aeli -> æ - new Transition (108, 109), // &amal -> &amalg - new Transition (119, 136), // &an -> &ang - new Transition (147, 160), // &angmsda -> &angmsdag - new Transition (183, 184), // &Ao -> &Aog - new Transition (188, 189), // &ao -> &aog - new Transition (239, 240), // &Arin -> Å - new Transition (244, 245), // &arin -> å - new Transition (256, 257), // &Assi -> &Assig - new Transition (307, 308), // &backcon -> &backcong - new Transition (355, 357), // &barwed -> &barwedg - new Transition (371, 372), // &bcon -> &bcong - new Transition (442, 443), // &bi -> &big - new Transition (485, 486), // &bigtrian -> &bigtriang - new Transition (509, 510), // &bigwed -> &bigwedg - new Transition (527, 528), // &blacklozen -> &blacklozeng - new Transition (542, 543), // &blacktrian -> &blacktriang - new Transition (558, 559), // &blacktriangleri -> &blacktrianglerig - new Transition (999, 1000), // &circlearrowri -> &circlearrowrig - new Transition (1086, 1087), // &ClockwiseContourInte -> &ClockwiseContourInteg - new Transition (1164, 1165), // &con -> &cong - new Transition (1171, 1172), // &Con -> &Cong - new Transition (1194, 1195), // &ContourInte -> &ContourInteg - new Transition (1250, 1251), // &CounterClockwiseContourInte -> &CounterClockwiseContourInteg - new Transition (1373, 1374), // &curlywed -> &curlywedg - new Transition (1394, 1395), // &curvearrowri -> &curvearrowrig - new Transition (1426, 1427), // &Da -> &Dag - new Transition (1427, 1428), // &Dag -> &Dagg - new Transition (1433, 1434), // &da -> &dag - new Transition (1434, 1435), // &dag -> &dagg - new Transition (1494, 1495), // &dda -> &ddag - new Transition (1495, 1496), // &ddag -> &ddagg - new Transition (1516, 1517), // &de -> ° - new Transition (1599, 1633), // &di -> &dig - new Transition (1740, 1741), // &doublebarwed -> &doublebarwedg - new Transition (1758, 1759), // &DoubleContourInte -> &DoubleContourInteg - new Transition (1787, 1788), // &DoubleLeftRi -> &DoubleLeftRig - new Transition (1802, 1803), // &DoubleLon -> &DoubleLong - new Transition (1815, 1816), // &DoubleLongLeftRi -> &DoubleLongLeftRig - new Transition (1826, 1827), // &DoubleLongRi -> &DoubleLongRig - new Transition (1837, 1838), // &DoubleRi -> &DoubleRig - new Transition (1945, 1946), // &downharpoonri -> &downharpoonrig - new Transition (1955, 1956), // &DownLeftRi -> &DownLeftRig - new Transition (1988, 1989), // &DownRi -> &DownRig - new Transition (2088, 2089), // &dwan -> &dwang - new Transition (2101, 2102), // &dzi -> &dzig - new Transition (2108, 2187), // &E -> &Eg - new Transition (2115, 2185), // &e -> &eg - new Transition (2290, 2291), // &en -> &eng - new Transition (2296, 2297), // &Eo -> &Eog - new Transition (2301, 2302), // &eo -> &eog - new Transition (2357, 2358), // &eqslant -> &eqslantg - new Transition (2508, 2509), // &fallin -> &falling - new Transition (2533, 2534), // &ffili -> &ffilig - new Transition (2537, 2538), // &ffli -> &fflig - new Transition (2541, 2542), // &fflli -> &ffllig - new Transition (2551, 2552), // &fili -> &filig - new Transition (2589, 2590), // &fjli -> &fjlig - new Transition (2597, 2598), // &flli -> &fllig - new Transition (2701, 2807), // &g -> &gg - new Transition (2708, 2805), // &G -> &Gg - new Transition (2807, 2809), // &gg -> &ggg - new Transition (3149, 3150), // &hookri -> &hookrig - new Transition (3236, 3289), // &I -> &Ig - new Transition (3243, 3295), // &i -> &ig - new Transition (3322, 3323), // &IJli -> &IJlig - new Transition (3327, 3328), // &ijli -> &ijlig - new Transition (3332, 3344), // &Ima -> &Imag - new Transition (3337, 3341), // &ima -> &imag - new Transition (3407, 3408), // &inte -> &integ - new Transition (3413, 3414), // &Inte -> &Integ - new Transition (3467, 3476), // &io -> &iog - new Transition (3471, 3472), // &Io -> &Iog - new Transition (3624, 3654), // &k -> &kg - new Transition (3692, 4317), // &l -> &lg - new Transition (3705, 3718), // &la -> &lag - new Transition (3733, 3734), // &Lan -> &Lang - new Transition (3736, 3737), // &lan -> &lang - new Transition (3894, 4183), // &lE -> &lEg - new Transition (3896, 4185), // &le -> &leg - new Transition (3902, 3903), // &LeftAn -> &LeftAng - new Transition (3938, 3939), // &LeftArrowRi -> &LeftArrowRig - new Transition (3958, 3959), // &LeftCeilin -> &LeftCeiling - new Transition (4031, 4032), // &LeftRi -> &LeftRig - new Transition (4042, 4043), // &Leftri -> &Leftrig - new Transition (4053, 4054), // &leftri -> &leftrig - new Transition (4077, 4078), // &leftrightsqui -> &leftrightsquig - new Transition (4123, 4124), // &LeftTrian -> &LeftTriang - new Transition (4197, 4210), // &les -> &lesg - new Transition (4215, 4271), // &less -> &lessg - new Transition (4228, 4229), // &lesseq -> &lesseqg - new Transition (4233, 4234), // &lesseqq -> &lesseqqg - new Transition (4424, 4425), // &loan -> &loang - new Transition (4435, 4436), // &Lon -> &Long - new Transition (4457, 4458), // &lon -> &long - new Transition (4470, 4471), // &LongLeftRi -> &LongLeftRig - new Transition (4481, 4482), // &Longleftri -> &Longleftrig - new Transition (4492, 4493), // &longleftri -> &longleftrig - new Transition (4510, 4511), // &LongRi -> &LongRig - new Transition (4521, 4522), // &Longri -> &Longrig - new Transition (4532, 4533), // &longri -> &longrig - new Transition (4555, 4556), // &looparrowri -> &looparrowrig - new Transition (4602, 4603), // &LowerRi -> &LowerRig - new Transition (4615, 4616), // &lozen -> &lozeng - new Transition (4670, 4674), // &lsim -> &lsimg - new Transition (4838, 4839), // &measuredan -> &measuredang - new Transition (4965, 5195), // &n -> &ng - new Transition (4983, 4984), // &nan -> &nang - new Transition (5045, 5046), // &ncon -> &ncong - new Transition (5084, 5085), // &Ne -> &Neg - new Transition (5212, 5213), // &nG -> &nGg - new Transition (5291, 5292), // &nLeftri -> &nLeftrig - new Transition (5302, 5303), // &nleftri -> &nleftrig - new Transition (5361, 5362), // &NonBreakin -> &NonBreaking - new Transition (5382, 5383), // &NotCon -> &NotCong - new Transition (5536, 5537), // &NotLeftTrian -> &NotLeftTriang - new Transition (5671, 5672), // &NotRi -> &NotRig - new Transition (5679, 5680), // &NotRightTrian -> &NotRightTriang - new Transition (5869, 5870), // &nRi -> &nRig - new Transition (5879, 5880), // &nri -> &nrig - new Transition (5988, 5989), // &nt -> &ntg - new Transition (6003, 6004), // &ntl -> &ntlg - new Transition (6009, 6010), // &ntrian -> &ntriang - new Transition (6022, 6023), // &ntriangleri -> &ntrianglerig - new Transition (6043, 6068), // &nv -> &nvg - new Transition (6131, 6214), // &O -> &Og - new Transition (6138, 6210), // &o -> &og - new Transition (6192, 6193), // &OEli -> &OElig - new Transition (6197, 6198), // &oeli -> &oelig - new Transition (6268, 6269), // &Ome -> &Omeg - new Transition (6272, 6273), // &ome -> &omeg - new Transition (6360, 6361), // &ori -> &orig - new Transition (6908, 6909), // &Ran -> &Rang - new Transition (6911, 6912), // &ran -> &rang - new Transition (7074, 7095), // &re -> ® - new Transition (7171, 7172), // &Ri -> &Rig - new Transition (7176, 7177), // &RightAn -> &RightAng - new Transition (7199, 7200), // &ri -> &rig - new Transition (7233, 7234), // &RightCeilin -> &RightCeiling - new Transition (7315, 7316), // &rightri -> &rightrig - new Transition (7329, 7330), // &rightsqui -> &rightsquig - new Transition (7368, 7369), // &RightTrian -> &RightTriang - new Transition (7428, 7429), // &rin -> &ring - new Transition (7433, 7434), // &risin -> &rising - new Transition (7471, 7472), // &roan -> &roang - new Transition (7514, 7516), // &rpar -> &rparg - new Transition (7532, 7533), // &Rri -> &Rrig - new Transition (7813, 7814), // &ShortRi -> &ShortRig - new Transition (7833, 7834), // &Si -> &Sig - new Transition (7838, 7839), // &si -> &sig - new Transition (7847, 7857), // &sim -> &simg - new Transition (8108, 8109), // &strai -> &straig - new Transition (8279, 8280), // &sun -> &sung - new Transition (8397, 8398), // &szli -> ß - new Transition (8406, 8407), // &tar -> &targ - new Transition (8635, 8636), // &trian -> &triang - new Transition (8656, 8657), // &triangleri -> &trianglerig - new Transition (8758, 8759), // &twoheadri -> &twoheadrig - new Transition (8768, 8860), // &U -> &Ug - new Transition (8775, 8866), // &u -> &ug - new Transition (8954, 8955), // &Uo -> &Uog - new Transition (8959, 8960), // &uo -> &uog - new Transition (9059, 9060), // &upharpoonri -> &upharpoonrig - new Transition (9082, 9083), // &UpperRi -> &UpperRig - new Transition (9142, 9143), // &Urin -> &Uring - new Transition (9146, 9147), // &urin -> &uring - new Transition (9196, 9197), // &uwan -> &uwang - new Transition (9203, 9204), // &van -> &vang - new Transition (9228, 9229), // &varnothin -> &varnothing - new Transition (9253, 9254), // &varsi -> &varsig - new Transition (9288, 9289), // &vartrian -> &vartriang - new Transition (9298, 9299), // &vartriangleri -> &vartrianglerig - new Transition (9478, 9479), // &vzi -> &vzig - new Transition (9481, 9482), // &vzigza -> &vzigzag - new Transition (9497, 9507), // &wed -> &wedg - new Transition (9503, 9504), // &Wed -> &Wedg - new Transition (9661, 9662), // &xwed -> &xwedg - new Transition (9825, 9826) // &zi -> &zig - }; - TransitionTable_h = new Transition[159] { - new Transition (0, 3020), // & -> &h - new Transition (86, 87), // &alep -> &aleph - new Transition (90, 91), // &Alp -> &Alph - new Transition (94, 95), // &alp -> &alph - new Transition (147, 162), // &angmsda -> &angmsdah - new Transition (173, 174), // &angsp -> &angsph - new Transition (338, 339), // &Backslas -> &Backslash - new Transition (426, 429), // &bet -> &beth - new Transition (559, 560), // &blacktrianglerig -> &blacktrianglerigh - new Transition (613, 638), // &box -> &boxh - new Transition (691, 697), // &boxV -> &boxVh - new Transition (693, 701), // &boxv -> &boxvh - new Transition (758, 762), // &bsol -> &bsolh - new Transition (789, 973), // &C -> &Ch - new Transition (796, 960), // &c -> &ch - new Transition (1000, 1001), // &circlearrowrig -> &circlearrowrigh - new Transition (1016, 1017), // &circleddas -> &circleddash - new Transition (1395, 1396), // &curvearrowrig -> &curvearrowrigh - new Transition (1432, 1550), // &d -> &dh - new Transition (1441, 1442), // &dalet -> &daleth - new Transition (1454, 1455), // &das -> &dash - new Transition (1457, 1458), // &Das -> &Dash - new Transition (1506, 1507), // &DDotra -> &DDotrah - new Transition (1537, 1538), // &dfis -> &dfish - new Transition (1788, 1789), // &DoubleLeftRig -> &DoubleLeftRigh - new Transition (1816, 1817), // &DoubleLongLeftRig -> &DoubleLongLeftRigh - new Transition (1827, 1828), // &DoubleLongRig -> &DoubleLongRigh - new Transition (1838, 1839), // &DoubleRig -> &DoubleRigh - new Transition (1896, 1932), // &down -> &downh - new Transition (1946, 1947), // &downharpoonrig -> &downharpoonrigh - new Transition (1956, 1957), // &DownLeftRig -> &DownLeftRigh - new Transition (1989, 1990), // &DownRig -> &DownRigh - new Transition (2077, 2082), // &du -> &duh - new Transition (2439, 2445), // &et -> ð - new Transition (3132, 3133), // &homt -> &homth - new Transition (3150, 3151), // &hookrig -> &hookrigh - new Transition (3194, 3195), // &hslas -> &hslash - new Transition (3231, 3232), // &hyp -> &hyph - new Transition (3362, 3363), // &imat -> &imath - new Transition (3435, 3436), // &intlar -> &intlarh - new Transition (3579, 3580), // &jmat -> &jmath - new Transition (3624, 3664), // &k -> &kh - new Transition (3692, 4325), // &l -> &lh - new Transition (3766, 3776), // &larr -> &larrh - new Transition (3880, 3881), // &ldrd -> &ldrdh - new Transition (3886, 3887), // &ldrus -> &ldrush - new Transition (3891, 3892), // &lds -> &ldsh - new Transition (3926, 4004), // &left -> &lefth - new Transition (3939, 3940), // &LeftArrowRig -> &LeftArrowRigh - new Transition (4032, 4033), // &LeftRig -> &LeftRigh - new Transition (4043, 4044), // &Leftrig -> &Leftrigh - new Transition (4054, 4055), // &leftrig -> &leftrigh - new Transition (4056, 4065), // &leftright -> &leftrighth - new Transition (4109, 4110), // &leftt -> &leftth - new Transition (4303, 4304), // &lfis -> &lfish - new Transition (4348, 4370), // &ll -> &llh - new Transition (4397, 4398), // &lmoustac -> &lmoustach - new Transition (4471, 4472), // &LongLeftRig -> &LongLeftRigh - new Transition (4482, 4483), // &Longleftrig -> &Longleftrigh - new Transition (4493, 4494), // &longleftrig -> &longleftrigh - new Transition (4511, 4512), // &LongRig -> &LongRigh - new Transition (4522, 4523), // &Longrig -> &Longrigh - new Transition (4533, 4534), // &longrig -> &longrigh - new Transition (4556, 4557), // &looparrowrig -> &looparrowrigh - new Transition (4603, 4604), // &LowerRig -> &LowerRigh - new Transition (4628, 4640), // &lr -> &lrh - new Transition (4652, 4667), // &ls -> &lsh - new Transition (4658, 4665), // &Ls -> &Lsh - new Transition (4698, 4710), // < -> <h - new Transition (4745, 4746), // &lurds -> &lurdsh - new Transition (4750, 4751), // &luru -> &luruh - new Transition (4767, 4868), // &m -> &mh - new Transition (4822, 4823), // &mdas -> &mdash - new Transition (4965, 5227), // &n -> &nh - new Transition (5061, 5062), // &ndas -> &ndash - new Transition (5067, 5068), // &near -> &nearh - new Transition (5103, 5104), // &NegativeT -> &NegativeTh - new Transition (5125, 5126), // &NegativeVeryT -> &NegativeVeryTh - new Transition (5292, 5293), // &nLeftrig -> &nLeftrigh - new Transition (5303, 5304), // &nleftrig -> &nleftrigh - new Transition (5672, 5673), // &NotRig -> &NotRigh - new Transition (5870, 5871), // &nRig -> &nRigh - new Transition (5880, 5881), // &nrig -> &nrigh - new Transition (5895, 5910), // &ns -> &nsh - new Transition (6023, 6024), // &ntrianglerig -> &ntrianglerigh - new Transition (6050, 6051), // &nVDas -> &nVDash - new Transition (6055, 6056), // &nVdas -> &nVdash - new Transition (6060, 6061), // &nvDas -> &nvDash - new Transition (6065, 6066), // &nvdas -> &nvdash - new Transition (6113, 6114), // &nwar -> &nwarh - new Transition (6138, 6227), // &o -> &oh - new Transition (6165, 6166), // &odas -> &odash - new Transition (6388, 6389), // &Oslas -> Ø - new Transition (6393, 6394), // &oslas -> ø - new Transition (6456, 6457), // &OverParent -> &OverParenth - new Transition (6463, 6527), // &p -> &ph - new Transition (6482, 6524), // &P -> &Ph - new Transition (6546, 6547), // &pitc -> &pitch - new Transition (6559, 6561), // &planck -> &planckh - new Transition (6876, 7155), // &r -> &rh - new Transition (6886, 7164), // &R -> &Rh - new Transition (6932, 6947), // &rarr -> &rarrh - new Transition (7058, 7059), // &rdld -> &rdldh - new Transition (7069, 7070), // &rds -> &rdsh - new Transition (7137, 7138), // &rfis -> &rfish - new Transition (7172, 7173), // &Rig -> &Righ - new Transition (7200, 7201), // &rig -> &righ - new Transition (7202, 7279), // &right -> &righth - new Transition (7297, 7305), // &rightleft -> &rightlefth - new Transition (7316, 7317), // &rightrig -> &rightrigh - new Transition (7354, 7355), // &rightt -> &rightth - new Transition (7442, 7447), // &rl -> &rlh - new Transition (7460, 7461), // &rmoustac -> &rmoustach - new Transition (7533, 7534), // &Rrig -> &Rrigh - new Transition (7542, 7557), // &rs -> &rsh - new Transition (7548, 7555), // &Rs -> &Rsh - new Transition (7567, 7568), // &rt -> &rth - new Transition (7603, 7604), // &rulu -> &ruluh - new Transition (7610, 7772), // &S -> &Sh - new Transition (7617, 7751), // &s -> &sh - new Transition (7705, 7706), // &sear -> &searh - new Transition (7762, 7763), // &shc -> &shch - new Transition (7814, 7815), // &ShortRig -> &ShortRigh - new Transition (7907, 7908), // &smas -> &smash - new Transition (8109, 8110), // &straig -> &straigh - new Transition (8120, 8121), // &straightp -> &straightph - new Transition (8216, 8269), // &Suc -> &Such - new Transition (8270, 8271), // &SuchT -> &SuchTh - new Transition (8284, 8320), // &sup -> &suph - new Transition (8377, 8378), // &swar -> &swarh - new Transition (8400, 8467), // &T -> &Th - new Transition (8404, 8461), // &t -> &th - new Transition (8657, 8658), // &trianglerig -> &trianglerigh - new Transition (8709, 8723), // &ts -> &tsh - new Transition (8742, 8743), // &two -> &twoh - new Transition (8759, 8760), // &twoheadrig -> &twoheadrigh - new Transition (8775, 8876), // &u -> &uh - new Transition (8829, 8845), // &ud -> &udh - new Transition (8851, 8852), // &ufis -> &ufish - new Transition (8938, 8939), // &UnderParent -> &UnderParenth - new Transition (8983, 9046), // &up -> &uph - new Transition (9060, 9061), // &upharpoonrig -> &upharpoonrigh - new Transition (9083, 9084), // &UpperRig -> &UpperRigh - new Transition (9096, 9098), // &upsi -> &upsih - new Transition (9225, 9226), // &varnot -> &varnoth - new Transition (9231, 9232), // &varp -> &varph - new Transition (9247, 9249), // &varr -> &varrh - new Transition (9279, 9280), // &vart -> &varth - new Transition (9299, 9300), // &vartrianglerig -> &vartrianglerigh - new Transition (9322, 9323), // &VDas -> &VDash - new Transition (9327, 9328), // &Vdas -> &Vdash - new Transition (9332, 9333), // &vDas -> &vDash - new Transition (9337, 9338), // &vdas -> &vdash - new Transition (9404, 9405), // &VeryT -> &VeryTh - new Transition (9474, 9475), // &Vvdas -> &Vvdash - new Transition (9537, 9538), // &wreat -> &wreath - new Transition (9548, 9572), // &x -> &xh - new Transition (9754, 9821), // &z -> &zh - new Transition (9797, 9798) // &ZeroWidt -> &ZeroWidth - }; - TransitionTable_i = new Transition[428] { - new Transition (0, 3243), // & -> &i - new Transition (27, 38), // &ac -> &aci - new Transition (33, 34), // &Ac -> &Aci - new Transition (51, 52), // &AEl -> &AEli - new Transition (56, 57), // &ael -> &aeli - new Transition (199, 210), // &ap -> &api - new Transition (202, 203), // &apac -> &apaci - new Transition (224, 225), // &ApplyFunct -> &ApplyFuncti - new Transition (237, 238), // &Ar -> &Ari - new Transition (242, 243), // &ar -> &ari - new Transition (255, 256), // &Ass -> &Assi - new Transition (269, 270), // &At -> &Ati - new Transition (275, 276), // &at -> &ati - new Transition (289, 297), // &aw -> &awi - new Transition (292, 293), // &awcon -> &awconi - new Transition (301, 442), // &b -> &bi - new Transition (312, 313), // &backeps -> &backepsi - new Transition (319, 320), // &backpr -> &backpri - new Transition (324, 325), // &backs -> &backsi - new Transition (406, 407), // &beps -> &bepsi - new Transition (419, 420), // &Bernoull -> &Bernoulli - new Transition (444, 448), // &bigc -> &bigci - new Transition (465, 466), // &bigot -> &bigoti - new Transition (482, 483), // &bigtr -> &bigtri - new Transition (539, 540), // &blacktr -> &blacktri - new Transition (557, 558), // &blacktriangler -> &blacktriangleri - new Transition (583, 584), // &bnequ -> &bnequi - new Transition (609, 610), // &bowt -> &bowti - new Transition (656, 657), // &boxm -> &boxmi - new Transition (667, 668), // &boxt -> &boxti - new Transition (720, 721), // &bpr -> &bpri - new Transition (744, 752), // &bs -> &bsi - new Transition (749, 750), // &bsem -> &bsemi - new Transition (789, 1019), // &C -> &Ci - new Transition (796, 978), // &c -> &ci - new Transition (803, 828), // &Cap -> &Capi - new Transition (832, 833), // &CapitalD -> &CapitalDi - new Transition (840, 841), // &CapitalDifferent -> &CapitalDifferenti - new Transition (861, 890), // &cc -> &cci - new Transition (866, 886), // &Cc -> &Cci - new Transition (877, 878), // &Cced -> &Ccedi - new Transition (882, 883), // &cced -> &ccedi - new Transition (895, 896), // &Ccon -> &Cconi - new Transition (916, 917), // &ced -> &cedi - new Transition (921, 922), // &Ced -> &Cedi - new Transition (960, 976), // &ch -> &chi - new Transition (973, 974), // &Ch -> &Chi - new Transition (998, 999), // &circlearrowr -> &circlearrowri - new Transition (1009, 1010), // &circledc -> &circledci - new Transition (1032, 1033), // &CircleM -> &CircleMi - new Transition (1043, 1044), // &CircleT -> &CircleTi - new Transition (1054, 1055), // &cirfn -> &cirfni - new Transition (1059, 1060), // &cirm -> &cirmi - new Transition (1064, 1065), // &cirsc -> &cirsci - new Transition (1072, 1073), // &Clockw -> &Clockwi - new Transition (1122, 1123), // &clubsu -> &clubsui - new Transition (1164, 1183), // &con -> &coni - new Transition (1171, 1179), // &Con -> &Coni - new Transition (1236, 1237), // &CounterClockw -> &CounterClockwi - new Transition (1393, 1394), // &curvearrowr -> &curvearrowri - new Transition (1407, 1415), // &cw -> &cwi - new Transition (1410, 1411), // &cwcon -> &cwconi - new Transition (1425, 1557), // &D -> &Di - new Transition (1432, 1599), // &d -> &di - new Transition (1535, 1536), // &df -> &dfi - new Transition (1560, 1561), // &Diacr -> &Diacri - new Transition (1562, 1563), // &Diacrit -> &Diacriti - new Transition (1593, 1594), // &DiacriticalT -> &DiacriticalTi - new Transition (1613, 1614), // &diamondsu -> &diamondsui - new Transition (1627, 1628), // &Different -> &Differenti - new Transition (1639, 1640), // &dis -> &disi - new Transition (1643, 1645), // &div -> &divi - new Transition (1651, 1652), // ÷ont -> ÷onti - new Transition (1713, 1714), // &dotm -> &dotmi - new Transition (1786, 1787), // &DoubleLeftR -> &DoubleLeftRi - new Transition (1814, 1815), // &DoubleLongLeftR -> &DoubleLongLeftRi - new Transition (1825, 1826), // &DoubleLongR -> &DoubleLongRi - new Transition (1836, 1837), // &DoubleR -> &DoubleRi - new Transition (1872, 1873), // &DoubleVert -> &DoubleVerti - new Transition (1944, 1945), // &downharpoonr -> &downharpoonri - new Transition (1954, 1955), // &DownLeftR -> &DownLeftRi - new Transition (1987, 1988), // &DownR -> &DownRi - new Transition (2072, 2073), // &dtr -> &dtri - new Transition (2097, 2101), // &dz -> &dzi - new Transition (2127, 2142), // &Ec -> &Eci - new Transition (2133, 2139), // &ec -> &eci - new Transition (2204, 2213), // &el -> &eli - new Transition (2323, 2324), // &eps -> &epsi - new Transition (2327, 2328), // &Eps -> &Epsi - new Transition (2340, 2341), // &eqc -> &eqci - new Transition (2350, 2351), // &eqs -> &eqsi - new Transition (2368, 2387), // &Equ -> &Equi - new Transition (2372, 2396), // &equ -> &equi - new Transition (2377, 2378), // &EqualT -> &EqualTi - new Transition (2388, 2389), // &Equil -> &Equili - new Transition (2391, 2392), // &Equilibr -> &Equilibri - new Transition (2418, 2430), // &Es -> &Esi - new Transition (2422, 2433), // &es -> &esi - new Transition (2458, 2462), // &ex -> &exi - new Transition (2466, 2467), // &Ex -> &Exi - new Transition (2477, 2478), // &expectat -> &expectati - new Transition (2487, 2488), // &Exponent -> &Exponenti - new Transition (2497, 2498), // &exponent -> &exponenti - new Transition (2503, 2549), // &f -> &fi - new Transition (2506, 2507), // &fall -> &falli - new Transition (2517, 2554), // &F -> &Fi - new Transition (2530, 2531), // &ff -> &ffi - new Transition (2532, 2533), // &ffil -> &ffili - new Transition (2536, 2537), // &ffl -> &ffli - new Transition (2540, 2541), // &ffll -> &fflli - new Transition (2550, 2551), // &fil -> &fili - new Transition (2588, 2589), // &fjl -> &fjli - new Transition (2596, 2597), // &fll -> &flli - new Transition (2631, 2632), // &Four -> &Fouri - new Transition (2642, 2643), // &fpart -> &fparti - new Transition (2701, 2811), // &g -> &gi - new Transition (2736, 2742), // &Gc -> &Gci - new Transition (2738, 2739), // &Gced -> &Gcedi - new Transition (2746, 2747), // &gc -> &gci - new Transition (2849, 2850), // &gns -> &gnsi - new Transition (2917, 2918), // &GreaterT -> &GreaterTi - new Transition (2927, 2931), // &gs -> &gsi - new Transition (2944, 2947), // >c -> >ci - new Transition (2998, 2999), // >rs -> >rsi - new Transition (3014, 3100), // &H -> &Hi - new Transition (3021, 3022), // &ha -> &hai - new Transition (3030, 3031), // &ham -> &hami - new Transition (3052, 3053), // &harrc -> &harrci - new Transition (3064, 3065), // &Hc -> &Hci - new Transition (3069, 3070), // &hc -> &hci - new Transition (3080, 3081), // &heartsu -> &heartsui - new Transition (3085, 3086), // &hell -> &helli - new Transition (3148, 3149), // &hookr -> &hookri - new Transition (3171, 3172), // &Hor -> &Hori - new Transition (3179, 3180), // &HorizontalL -> &HorizontalLi - new Transition (3243, 3301), // &i -> &ii - new Transition (3250, 3257), // &ic -> &ici - new Transition (3252, 3253), // &Ic -> &Ici - new Transition (3301, 3303), // &ii -> &iii - new Transition (3303, 3304), // &iii -> &iiii - new Transition (3312, 3313), // &iinf -> &iinfi - new Transition (3321, 3322), // &IJl -> &IJli - new Transition (3326, 3327), // &ijl -> &ijli - new Transition (3344, 3345), // &Imag -> &Imagi - new Transition (3352, 3353), // &imagl -> &imagli - new Transition (3373, 3374), // &Impl -> &Impli - new Transition (3385, 3386), // &inf -> &infi - new Transition (3389, 3390), // &infint -> &infinti - new Transition (3428, 3429), // &Intersect -> &Intersecti - new Transition (3444, 3445), // &Inv -> &Invi - new Transition (3446, 3447), // &Invis -> &Invisi - new Transition (3457, 3458), // &InvisibleT -> &InvisibleTi - new Transition (3507, 3511), // &is -> &isi - new Transition (3526, 3534), // &it -> &iti - new Transition (3528, 3529), // &It -> &Iti - new Transition (3556, 3557), // &Jc -> &Jci - new Transition (3562, 3563), // &jc -> &jci - new Transition (3634, 3635), // &Kced -> &Kcedi - new Transition (3640, 3641), // &kced -> &kcedi - new Transition (3785, 3786), // &larrs -> &larrsi - new Transition (3795, 3796), // &lAta -> &lAtai - new Transition (3799, 3800), // &lata -> &latai - new Transition (3850, 3851), // &Lced -> &Lcedi - new Transition (3854, 3859), // &lce -> &lcei - new Transition (3855, 3856), // &lced -> &lcedi - new Transition (3937, 3938), // &LeftArrowR -> &LeftArrowRi - new Transition (3949, 3950), // &leftarrowta -> &leftarrowtai - new Transition (3954, 3955), // &LeftCe -> &LeftCei - new Transition (3956, 3957), // &LeftCeil -> &LeftCeili - new Transition (4030, 4031), // &LeftR -> &LeftRi - new Transition (4041, 4042), // &Leftr -> &Leftri - new Transition (4052, 4053), // &leftr -> &leftri - new Transition (4076, 4077), // &leftrightsqu -> &leftrightsqui - new Transition (4114, 4115), // &leftthreet -> &leftthreeti - new Transition (4120, 4121), // &LeftTr -> &LeftTri - new Transition (4280, 4281), // &lesss -> &lesssi - new Transition (4295, 4296), // &LessT -> &LessTi - new Transition (4301, 4302), // &lf -> &lfi - new Transition (4376, 4377), // &lltr -> &lltri - new Transition (4379, 4380), // &Lm -> &Lmi - new Transition (4385, 4386), // &lm -> &lmi - new Transition (4418, 4419), // &lns -> &lnsi - new Transition (4469, 4470), // &LongLeftR -> &LongLeftRi - new Transition (4480, 4481), // &Longleftr -> &Longleftri - new Transition (4491, 4492), // &longleftr -> &longleftri - new Transition (4509, 4510), // &LongR -> &LongRi - new Transition (4520, 4521), // &Longr -> &Longri - new Transition (4531, 4532), // &longr -> &longri - new Transition (4554, 4555), // &looparrowr -> &looparrowri - new Transition (4573, 4574), // &lot -> &loti - new Transition (4601, 4602), // &LowerR -> &LowerRi - new Transition (4649, 4650), // &lrtr -> &lrtri - new Transition (4652, 4669), // &ls -> &lsi - new Transition (4698, 4715), // < -> <i - new Transition (4700, 4703), // <c -> <ci - new Transition (4731, 4732), // <r -> <ri - new Transition (4767, 4871), // &m -> &mi - new Transition (4781, 4900), // &M -> &Mi - new Transition (4844, 4845), // &Med -> &Medi - new Transition (4855, 4856), // &Mell -> &Melli - new Transition (4882, 4883), // &midc -> &midci - new Transition (4955, 4956), // &mult -> &multi - new Transition (4965, 5240), // &n -> &ni - new Transition (4986, 4990), // &nap -> &napi - new Transition (5035, 5036), // &Nced -> &Ncedi - new Transition (5040, 5041), // &nced -> &ncedi - new Transition (5087, 5088), // &Negat -> &Negati - new Transition (5093, 5094), // &NegativeMed -> &NegativeMedi - new Transition (5104, 5105), // &NegativeTh -> &NegativeThi - new Transition (5126, 5127), // &NegativeVeryTh -> &NegativeVeryThi - new Transition (5136, 5137), // &nequ -> &nequi - new Transition (5140, 5145), // &nes -> &nesi - new Transition (5177, 5178), // &NewL -> &NewLi - new Transition (5182, 5183), // &nex -> &nexi - new Transition (5215, 5216), // &ngs -> &ngsi - new Transition (5290, 5291), // &nLeftr -> &nLeftri - new Transition (5301, 5302), // &nleftr -> &nleftri - new Transition (5328, 5329), // &nls -> &nlsi - new Transition (5336, 5337), // &nltr -> &nltri - new Transition (5343, 5344), // &nm -> &nmi - new Transition (5359, 5360), // &NonBreak -> &NonBreaki - new Transition (5378, 5512), // ¬ -> ¬i - new Transition (5405, 5406), // &NotDoubleVert -> &NotDoubleVerti - new Transition (5427, 5428), // &NotEqualT -> &NotEqualTi - new Transition (5433, 5434), // &NotEx -> &NotExi - new Transition (5487, 5488), // &NotGreaterT -> &NotGreaterTi - new Transition (5533, 5534), // &NotLeftTr -> &NotLeftTri - new Transition (5584, 5585), // &NotLessT -> &NotLessTi - new Transition (5620, 5621), // ¬n -> ¬ni - new Transition (5656, 5671), // &NotR -> &NotRi - new Transition (5676, 5677), // &NotRightTr -> &NotRightTri - new Transition (5762, 5763), // &NotSucceedsT -> &NotSucceedsTi - new Transition (5781, 5782), // &NotT -> &NotTi - new Transition (5803, 5804), // &NotTildeT -> &NotTildeTi - new Transition (5812, 5813), // &NotVert -> &NotVerti - new Transition (5837, 5838), // &npol -> &npoli - new Transition (5855, 5879), // &nr -> &nri - new Transition (5868, 5869), // &nR -> &nRi - new Transition (5890, 5891), // &nrtr -> &nrtri - new Transition (5895, 5927), // &ns -> &nsi - new Transition (5914, 5915), // &nshortm -> &nshortmi - new Transition (5934, 5935), // &nsm -> &nsmi - new Transition (5988, 5998), // &nt -> &nti - new Transition (5992, 5993), // &Nt -> &Nti - new Transition (6006, 6007), // &ntr -> &ntri - new Transition (6021, 6022), // &ntriangler -> &ntriangleri - new Transition (6043, 6078), // &nv -> &nvi - new Transition (6080, 6081), // &nvinf -> &nvinfi - new Transition (6093, 6094), // &nvltr -> &nvltri - new Transition (6103, 6104), // &nvrtr -> &nvrtri - new Transition (6107, 6108), // &nvs -> &nvsi - new Transition (6138, 6234), // &o -> &oi - new Transition (6148, 6149), // &oc -> &oci - new Transition (6152, 6153), // &Oc -> &Oci - new Transition (6163, 6179), // &od -> &odi - new Transition (6191, 6192), // &OEl -> &OEli - new Transition (6196, 6197), // &oel -> &oeli - new Transition (6201, 6202), // &ofc -> &ofci - new Transition (6238, 6252), // &ol -> &oli - new Transition (6243, 6244), // &olc -> &olci - new Transition (6258, 6276), // &Om -> &Omi - new Transition (6263, 6282), // &om -> &omi - new Transition (6342, 6360), // &or -> &ori - new Transition (6399, 6400), // &Ot -> &Oti - new Transition (6405, 6406), // &ot -> &oti - new Transition (6459, 6460), // &OverParenthes -> &OverParenthesi - new Transition (6463, 6543), // &p -> &pi - new Transition (6474, 6475), // &pars -> &parsi - new Transition (6482, 6541), // &P -> &Pi - new Transition (6485, 6486), // &Part -> &Parti - new Transition (6498, 6503), // &per -> &peri - new Transition (6507, 6508), // &perm -> &permi - new Transition (6524, 6525), // &Ph -> &Phi - new Transition (6527, 6528), // &ph -> &phi - new Transition (6570, 6571), // &plusac -> &plusaci - new Transition (6576, 6577), // &plusc -> &plusci - new Transition (6590, 6591), // &PlusM -> &PlusMi - new Transition (6599, 6600), // &pluss -> &plussi - new Transition (6609, 6610), // &Po -> &Poi - new Transition (6622, 6623), // &po -> &poi - new Transition (6625, 6626), // &point -> &pointi - new Transition (6640, 6725), // &Pr -> &Pri - new Transition (6642, 6729), // &pr -> &pri - new Transition (6696, 6697), // &PrecedesT -> &PrecedesTi - new Transition (6717, 6718), // &precns -> &precnsi - new Transition (6721, 6722), // &precs -> &precsi - new Transition (6741, 6742), // &prns -> &prnsi - new Transition (6760, 6761), // &profl -> &profli - new Transition (6775, 6776), // &Proport -> &Proporti - new Transition (6786, 6787), // &prs -> &prsi - new Transition (6795, 6803), // &Ps -> &Psi - new Transition (6799, 6805), // &ps -> &psi - new Transition (6817, 6821), // &q -> &qi - new Transition (6834, 6835), // &qpr -> &qpri - new Transition (6849, 6858), // &quat -> &quati - new Transition (6852, 6853), // &quatern -> &quaterni - new Transition (6876, 7199), // &r -> &ri - new Transition (6886, 7171), // &R -> &Ri - new Transition (6897, 6898), // &rad -> &radi - new Transition (6956, 6957), // &rarrs -> &rarrsi - new Transition (6969, 6970), // &rAta -> &rAtai - new Transition (6973, 6978), // &rat -> &rati - new Transition (6974, 6975), // &rata -> &ratai - new Transition (7034, 7035), // &Rced -> &Rcedi - new Transition (7038, 7043), // &rce -> &rcei - new Transition (7039, 7040), // &rced -> &rcedi - new Transition (7076, 7078), // &real -> &reali - new Transition (7111, 7112), // &ReverseEqu -> &ReverseEqui - new Transition (7113, 7114), // &ReverseEquil -> &ReverseEquili - new Transition (7116, 7117), // &ReverseEquilibr -> &ReverseEquilibri - new Transition (7125, 7126), // &ReverseUpEqu -> &ReverseUpEqui - new Transition (7127, 7128), // &ReverseUpEquil -> &ReverseUpEquili - new Transition (7130, 7131), // &ReverseUpEquilibr -> &ReverseUpEquilibri - new Transition (7135, 7136), // &rf -> &rfi - new Transition (7224, 7225), // &rightarrowta -> &rightarrowtai - new Transition (7229, 7230), // &RightCe -> &RightCei - new Transition (7231, 7232), // &RightCeil -> &RightCeili - new Transition (7314, 7315), // &rightr -> &rightri - new Transition (7328, 7329), // &rightsqu -> &rightsqui - new Transition (7359, 7360), // &rightthreet -> &rightthreeti - new Transition (7365, 7366), // &RightTr -> &RightTri - new Transition (7431, 7432), // &ris -> &risi - new Transition (7465, 7466), // &rnm -> &rnmi - new Transition (7495, 7496), // &rot -> &roti - new Transition (7507, 7508), // &RoundImpl -> &RoundImpli - new Transition (7521, 7522), // &rppol -> &rppoli - new Transition (7531, 7532), // &Rr -> &Rri - new Transition (7567, 7573), // &rt -> &rti - new Transition (7578, 7579), // &rtr -> &rtri - new Transition (7587, 7588), // &rtriltr -> &rtriltri - new Transition (7610, 7833), // &S -> &Si - new Transition (7617, 7838), // &s -> &si - new Transition (7629, 7662), // &Sc -> &Sci - new Transition (7631, 7666), // &sc -> &sci - new Transition (7654, 7655), // &Sced -> &Scedi - new Transition (7658, 7659), // &sced -> &scedi - new Transition (7676, 7677), // &scns -> &scnsi - new Transition (7682, 7683), // &scpol -> &scpoli - new Transition (7687, 7688), // &scs -> &scsi - new Transition (7721, 7722), // &sem -> &semi - new Transition (7730, 7731), // &setm -> &setmi - new Transition (7799, 7800), // &shortm -> &shortmi - new Transition (7812, 7813), // &ShortR -> &ShortRi - new Transition (7887, 7888), // &SmallC -> &SmallCi - new Transition (7894, 7918), // &sm -> &smi - new Transition (7901, 7902), // &smallsetm -> &smallsetmi - new Transition (7962, 7963), // &spadesu -> &spadesui - new Transition (8027, 8028), // &SquareIntersect -> &SquareIntersecti - new Transition (8059, 8060), // &SquareUn -> &SquareUni - new Transition (8086, 8087), // &ssm -> &ssmi - new Transition (8107, 8108), // &stra -> &strai - new Transition (8114, 8115), // &straighteps -> &straightepsi - new Transition (8121, 8122), // &straightph -> &straightphi - new Transition (8169, 8190), // &subs -> &subsi - new Transition (8240, 8241), // &SucceedsT -> &SucceedsTi - new Transition (8261, 8262), // &succns -> &succnsi - new Transition (8265, 8266), // &succs -> &succsi - new Transition (8352, 8367), // &sups -> &supsi - new Transition (8396, 8397), // &szl -> &szli - new Transition (8400, 8544), // &T -> &Ti - new Transition (8404, 8549), // &t -> &ti - new Transition (8432, 8433), // &Tced -> &Tcedi - new Transition (8437, 8438), // &tced -> &tcedi - new Transition (8461, 8493), // &th -> &thi - new Transition (8467, 8507), // &Th -> &Thi - new Transition (8503, 8504), // &thicks -> &thicksi - new Transition (8531, 8532), // &thks -> &thksi - new Transition (8570, 8571), // &TildeT -> &TildeTi - new Transition (8600, 8601), // &topc -> &topci - new Transition (8618, 8619), // &tpr -> &tpri - new Transition (8628, 8633), // &tr -> &tri - new Transition (8655, 8656), // &triangler -> &triangleri - new Transition (8670, 8671), // &trim -> &trimi - new Transition (8676, 8677), // &Tr -> &Tri - new Transition (8693, 8694), // &trit -> &triti - new Transition (8700, 8701), // &trpez -> &trpezi - new Transition (8737, 8738), // &tw -> &twi - new Transition (8757, 8758), // &twoheadr -> &twoheadri - new Transition (8793, 8794), // &Uarroc -> &Uarroci - new Transition (8815, 8816), // &Uc -> &Uci - new Transition (8820, 8821), // &uc -> &uci - new Transition (8849, 8850), // &uf -> &ufi - new Transition (8901, 8902), // &ultr -> &ultri - new Transition (8916, 8945), // &Un -> &Uni - new Transition (8941, 8942), // &UnderParenthes -> &UnderParenthesi - new Transition (9036, 9037), // &UpEqu -> &UpEqui - new Transition (9038, 9039), // &UpEquil -> &UpEquili - new Transition (9041, 9042), // &UpEquilibr -> &UpEquilibri - new Transition (9058, 9059), // &upharpoonr -> &upharpoonri - new Transition (9081, 9082), // &UpperR -> &UpperRi - new Transition (9092, 9093), // &Ups -> &Upsi - new Transition (9095, 9096), // &ups -> &upsi - new Transition (9127, 9145), // &ur -> &uri - new Transition (9140, 9141), // &Ur -> &Uri - new Transition (9150, 9151), // &urtr -> &urtri - new Transition (9161, 9172), // &ut -> &uti - new Transition (9166, 9167), // &Ut -> &Uti - new Transition (9177, 9178), // &utr -> &utri - new Transition (9211, 9212), // &vareps -> &varepsi - new Transition (9226, 9227), // &varnoth -> &varnothi - new Transition (9231, 9235), // &varp -> &varpi - new Transition (9232, 9233), // &varph -> &varphi - new Transition (9252, 9253), // &vars -> &varsi - new Transition (9285, 9286), // &vartr -> &vartri - new Transition (9297, 9298), // &vartriangler -> &vartriangleri - new Transition (9356, 9357), // &vell -> &velli - new Transition (9370, 9374), // &Vert -> &Verti - new Transition (9382, 9383), // &VerticalL -> &VerticalLi - new Transition (9397, 9398), // &VerticalT -> &VerticalTi - new Transition (9405, 9406), // &VeryTh -> &VeryThi - new Transition (9422, 9423), // &vltr -> &vltri - new Transition (9447, 9448), // &vrtr -> &vrtri - new Transition (9477, 9478), // &vz -> &vzi - new Transition (9485, 9486), // &Wc -> &Wci - new Transition (9491, 9492), // &wc -> &wci - new Transition (9496, 9512), // &we -> &wei - new Transition (9548, 9583), // &x -> &xi - new Transition (9549, 9553), // &xc -> &xci - new Transition (9562, 9563), // &xdtr -> &xdtri - new Transition (9565, 9581), // &X -> &Xi - new Transition (9598, 9599), // &xn -> &xni - new Transition (9618, 9619), // &xot -> &xoti - new Transition (9652, 9653), // &xutr -> &xutri - new Transition (9672, 9712), // &y -> &yi - new Transition (9685, 9686), // &Yc -> &Yci - new Transition (9690, 9691), // &yc -> &yci - new Transition (9754, 9825), // &z -> &zi - new Transition (9794, 9795) // &ZeroW -> &ZeroWi - }; - TransitionTable_j = new Transition[11] { - new Transition (0, 3561), // & -> &j - new Transition (1432, 1665), // &d -> &dj - new Transition (2503, 2587), // &f -> &fj - new Transition (2701, 2820), // &g -> &gj - new Transition (2824, 2830), // &gl -> &glj - new Transition (3243, 3325), // &i -> &ij - new Transition (3624, 3672), // &k -> &kj - new Transition (3692, 4342), // &l -> &lj - new Transition (4965, 5252), // &n -> &nj - new Transition (9848, 9849), // &zw -> &zwj - new Transition (9851, 9852) // &zwn -> &zwnj - }; - TransitionTable_k = new Transition[69] { - new Transition (0, 3624), // & -> &k - new Transition (301, 513), // &b -> &bk - new Transition (303, 304), // &bac -> &back - new Transition (333, 334), // &Bac -> &Back - new Transition (361, 362), // &bbr -> &bbrk - new Transition (366, 367), // &bbrktbr -> &bbrktbrk - new Transition (519, 566), // &bl -> &blk - new Transition (521, 522), // &blac -> &black - new Transition (563, 564), // &blan -> &blank - new Transition (576, 577), // &bloc -> &block - new Transition (965, 966), // &chec -> &check - new Transition (970, 971), // &checkmar -> &checkmark - new Transition (1070, 1071), // &Cloc -> &Clock - new Transition (1234, 1235), // &CounterCloc -> &CounterClock - new Transition (1463, 1464), // &db -> &dbk - new Transition (2024, 2025), // &drb -> &drbk - new Transition (2059, 2060), // &Dstro -> &Dstrok - new Transition (2064, 2065), // &dstro -> &dstrok - new Transition (2621, 2626), // &for -> &fork - new Transition (3017, 3018), // &Hace -> &Hacek - new Transition (3020, 3112), // &h -> &hk - new Transition (3136, 3137), // &hoo -> &hook - new Transition (3199, 3200), // &Hstro -> &Hstrok - new Transition (3204, 3205), // &hstro -> &hstrok - new Transition (3436, 3437), // &intlarh -> &intlarhk - new Transition (3539, 3540), // &Iu -> &Iuk - new Transition (3544, 3545), // &iu -> &iuk - new Transition (3608, 3609), // &Ju -> &Juk - new Transition (3613, 3614), // &ju -> &juk - new Transition (3776, 3777), // &larrh -> &larrhk - new Transition (3818, 3819), // &lbbr -> &lbbrk - new Transition (3821, 3828), // &lbr -> &lbrk - new Transition (3823, 3826), // &lbrac -> &lbrack - new Transition (3909, 3910), // &LeftAngleBrac -> &LeftAngleBrack - new Transition (3970, 3971), // &LeftDoubleBrac -> &LeftDoubleBrack - new Transition (4335, 4336), // &lhbl -> &lhblk - new Transition (4431, 4432), // &lobr -> &lobrk - new Transition (4686, 4687), // &Lstro -> &Lstrok - new Transition (4691, 4692), // &lstro -> &lstrok - new Transition (4804, 4805), // &mar -> &mark - new Transition (5068, 5069), // &nearh -> &nearhk - new Transition (5106, 5107), // &NegativeThic -> &NegativeThick - new Transition (5351, 5352), // &NoBrea -> &NoBreak - new Transition (5358, 5359), // &NonBrea -> &NonBreak - new Transition (6114, 6115), // &nwarh -> &nwarhk - new Transition (6444, 6447), // &OverBrac -> &OverBrack - new Transition (6515, 6516), // &perten -> &pertenk - new Transition (6550, 6551), // &pitchfor -> &pitchfork - new Transition (6557, 6563), // &plan -> &plank - new Transition (6558, 6559), // &planc -> &planck - new Transition (6947, 6948), // &rarrh -> &rarrhk - new Transition (7002, 7003), // &rbbr -> &rbbrk - new Transition (7005, 7012), // &rbr -> &rbrk - new Transition (7007, 7010), // &rbrac -> &rbrack - new Transition (7183, 7184), // &RightAngleBrac -> &RightAngleBrack - new Transition (7245, 7246), // &RightDoubleBrac -> &RightDoubleBrack - new Transition (7478, 7479), // &robr -> &robrk - new Transition (7706, 7707), // &searh -> &searhk - new Transition (8378, 8379), // &swarh -> &swarhk - new Transition (8416, 8417), // &tbr -> &tbrk - new Transition (8461, 8527), // &th -> &thk - new Transition (8494, 8495), // &thic -> &thick - new Transition (8508, 8509), // &Thic -> &Thick - new Transition (8611, 8612), // &topfor -> &topfork - new Transition (8729, 8730), // &Tstro -> &Tstrok - new Transition (8734, 8735), // &tstro -> &tstrok - new Transition (8884, 8885), // &uhbl -> &uhblk - new Transition (8926, 8929), // &UnderBrac -> &UnderBrack - new Transition (9208, 9217) // &var -> &vark - }; - TransitionTable_l = new Transition[438] { - new Transition (0, 3692), // & -> &l - new Transition (1, 89), // &A -> &Al - new Transition (8, 79), // &a -> &al - new Transition (50, 51), // &AE -> &AEl - new Transition (55, 56), // &ae -> &ael - new Transition (104, 108), // &ama -> &amal - new Transition (128, 129), // &ands -> &andsl - new Transition (136, 140), // &ang -> &angl - new Transition (217, 218), // &App -> &Appl - new Transition (270, 271), // &Ati -> &Atil - new Transition (276, 277), // &ati -> &atil - new Transition (282, 283), // &Aum -> Ä - new Transition (286, 287), // &aum -> ä - new Transition (301, 519), // &b -> &bl - new Transition (313, 314), // &backepsi -> &backepsil - new Transition (335, 336), // &Backs -> &Backsl - new Transition (417, 418), // &Bernou -> &Bernoul - new Transition (418, 419), // &Bernoul -> &Bernoull - new Transition (460, 461), // &bigop -> &bigopl - new Transition (486, 487), // &bigtriang -> &bigtriangl - new Transition (498, 499), // &bigup -> &bigupl - new Transition (522, 523), // &black -> &blackl - new Transition (543, 544), // &blacktriang -> &blacktriangl - new Transition (545, 552), // &blacktriangle -> &blacktrianglel - new Transition (618, 621), // &boxD -> &boxDl - new Transition (623, 626), // &boxd -> &boxdl - new Transition (662, 663), // &boxp -> &boxpl - new Transition (673, 676), // &boxU -> &boxUl - new Transition (678, 681), // &boxu -> &boxul - new Transition (691, 705), // &boxV -> &boxVl - new Transition (693, 709), // &boxv -> &boxvl - new Transition (757, 758), // &bso -> &bsol - new Transition (767, 768), // &bu -> &bul - new Transition (768, 769), // &bul -> &bull - new Transition (789, 1068), // &C -> &Cl - new Transition (796, 1117), // &c -> &cl - new Transition (830, 831), // &Capita -> &Capital - new Transition (842, 843), // &CapitalDifferentia -> &CapitalDifferential - new Transition (855, 856), // &Cay -> &Cayl - new Transition (878, 879), // &Ccedi -> Ç - new Transition (883, 884), // &ccedi -> ç - new Transition (917, 918), // &cedi -> ¸ - new Transition (922, 923), // &Cedi -> &Cedil - new Transition (923, 924), // &Cedil -> &Cedill - new Transition (981, 986), // &circ -> &circl - new Transition (992, 993), // &circlearrow -> &circlearrowl - new Transition (1021, 1022), // &Circ -> &Circl - new Transition (1038, 1039), // &CircleP -> &CirclePl - new Transition (1089, 1090), // &ClockwiseContourIntegra -> &ClockwiseContourIntegral - new Transition (1096, 1097), // &CloseCur -> &CloseCurl - new Transition (1102, 1103), // &CloseCurlyDoub -> &CloseCurlyDoubl - new Transition (1126, 1127), // &Co -> &Col - new Transition (1131, 1132), // &co -> &col - new Transition (1148, 1153), // &comp -> &compl - new Transition (1197, 1198), // &ContourIntegra -> &ContourIntegral - new Transition (1231, 1232), // &CounterC -> &CounterCl - new Transition (1253, 1254), // &CounterClockwiseContourIntegra -> &CounterClockwiseContourIntegral - new Transition (1292, 1308), // &cu -> &cul - new Transition (1296, 1297), // &cudarr -> &cudarrl - new Transition (1346, 1353), // &cur -> &curl - new Transition (1387, 1388), // &curvearrow -> &curvearrowl - new Transition (1419, 1420), // &cy -> &cyl - new Transition (1432, 1669), // &d -> &dl - new Transition (1433, 1439), // &da -> &dal - new Transition (1463, 1470), // &db -> &dbl - new Transition (1516, 1525), // &de -> &del - new Transition (1519, 1520), // &De -> &Del - new Transition (1552, 1553), // &dhar -> &dharl - new Transition (1565, 1566), // &Diacritica -> &Diacritical - new Transition (1578, 1579), // &DiacriticalDoub -> &DiacriticalDoubl - new Transition (1594, 1595), // &DiacriticalTi -> &DiacriticalTil - new Transition (1629, 1630), // &Differentia -> &Differential - new Transition (1679, 1680), // &do -> &dol - new Transition (1680, 1681), // &dol -> &doll - new Transition (1710, 1711), // &DotEqua -> &DotEqual - new Transition (1719, 1720), // &dotp -> &dotpl - new Transition (1732, 1733), // &doub -> &doubl - new Transition (1745, 1746), // &Doub -> &Doubl - new Transition (1761, 1762), // &DoubleContourIntegra -> &DoubleContourIntegral - new Transition (1875, 1876), // &DoubleVertica -> &DoubleVertical - new Transition (1938, 1939), // &downharpoon -> &downharpoonl - new Transition (2054, 2055), // &dso -> &dsol - new Transition (2089, 2090), // &dwang -> &dwangl - new Transition (2108, 2206), // &E -> &El - new Transition (2115, 2204), // &e -> &el - new Transition (2148, 2149), // &eco -> &ecol - new Transition (2204, 2220), // &el -> &ell - new Transition (2251, 2252), // &EmptySma -> &EmptySmal - new Transition (2252, 2253), // &EmptySmal -> &EmptySmall - new Transition (2269, 2270), // &EmptyVerySma -> &EmptyVerySmal - new Transition (2270, 2271), // &EmptyVerySmal -> &EmptyVerySmall - new Transition (2312, 2319), // &ep -> &epl - new Transition (2316, 2317), // &epars -> &eparsl - new Transition (2324, 2333), // &epsi -> &epsil - new Transition (2328, 2329), // &Epsi -> &Epsil - new Transition (2345, 2346), // &eqco -> &eqcol - new Transition (2350, 2354), // &eqs -> &eqsl - new Transition (2357, 2362), // &eqslant -> &eqslantl - new Transition (2369, 2370), // &Equa -> &Equal - new Transition (2373, 2374), // &equa -> &equal - new Transition (2378, 2379), // &EqualTi -> &EqualTil - new Transition (2387, 2388), // &Equi -> &Equil - new Transition (2406, 2407), // &eqvpars -> &eqvparsl - new Transition (2448, 2449), // &Eum -> Ë - new Transition (2452, 2453), // &eum -> ë - new Transition (2459, 2460), // &exc -> &excl - new Transition (2489, 2490), // &Exponentia -> &Exponential - new Transition (2499, 2500), // &exponentia -> &exponential - new Transition (2503, 2592), // &f -> &fl - new Transition (2504, 2505), // &fa -> &fal - new Transition (2505, 2506), // &fal -> &fall - new Transition (2526, 2527), // &fema -> &femal - new Transition (2530, 2536), // &ff -> &ffl - new Transition (2531, 2532), // &ffi -> &ffil - new Transition (2536, 2540), // &ffl -> &ffll - new Transition (2549, 2550), // &fi -> &fil - new Transition (2554, 2555), // &Fi -> &Fil - new Transition (2555, 2556), // &Fil -> &Fill - new Transition (2561, 2562), // &FilledSma -> &FilledSmal - new Transition (2562, 2563), // &FilledSmal -> &FilledSmall - new Transition (2577, 2578), // &FilledVerySma -> &FilledVerySmal - new Transition (2578, 2579), // &FilledVerySmal -> &FilledVerySmall - new Transition (2587, 2588), // &fj -> &fjl - new Transition (2592, 2596), // &fl -> &fll - new Transition (2617, 2618), // &ForA -> &ForAl - new Transition (2618, 2619), // &ForAl -> &ForAll - new Transition (2622, 2623), // &fora -> &foral - new Transition (2623, 2624), // &foral -> &forall - new Transition (2686, 2687), // &fras -> &frasl - new Transition (2701, 2824), // &g -> &gl - new Transition (2739, 2740), // &Gcedi -> &Gcedil - new Transition (2763, 2767), // &gE -> &gEl - new Transition (2765, 2769), // &ge -> &gel - new Transition (2775, 2776), // &geqs -> &geqsl - new Transition (2781, 2794), // &ges -> &gesl - new Transition (2790, 2792), // &gesdoto -> &gesdotol - new Transition (2813, 2814), // &gime -> &gimel - new Transition (2875, 2876), // &GreaterEqua -> &GreaterEqual - new Transition (2884, 2885), // &GreaterFu -> &GreaterFul - new Transition (2885, 2886), // &GreaterFul -> &GreaterFull - new Transition (2890, 2891), // &GreaterFullEqua -> &GreaterFullEqual - new Transition (2906, 2907), // &GreaterS -> &GreaterSl - new Transition (2914, 2915), // &GreaterSlantEqua -> &GreaterSlantEqual - new Transition (2918, 2919), // &GreaterTi -> &GreaterTil - new Transition (2932, 2936), // &gsim -> &gsiml - new Transition (2942, 2954), // > -> >l - new Transition (2965, 2993), // >r -> >rl - new Transition (2981, 2982), // >req -> >reql - new Transition (2987, 2988), // >reqq -> >reqql - new Transition (3021, 3027), // &ha -> &hal - new Transition (3031, 3032), // &hami -> &hamil - new Transition (3074, 3084), // &he -> &hel - new Transition (3084, 3085), // &hel -> &hell - new Transition (3100, 3101), // &Hi -> &Hil - new Transition (3137, 3138), // &hook -> &hookl - new Transition (3177, 3178), // &Horizonta -> &Horizontal - new Transition (3188, 3192), // &hs -> &hsl - new Transition (3222, 3223), // &HumpEqua -> &HumpEqual - new Transition (3227, 3228), // &hybu -> &hybul - new Transition (3228, 3229), // &hybul -> &hybull - new Transition (3278, 3279), // &iexc -> ¡ - new Transition (3320, 3321), // &IJ -> &IJl - new Transition (3325, 3326), // &ij -> &ijl - new Transition (3341, 3352), // &imag -> &imagl - new Transition (3372, 3373), // &Imp -> &Impl - new Transition (3401, 3433), // &int -> &intl - new Transition (3404, 3405), // &intca -> &intcal - new Transition (3416, 3417), // &Integra -> &Integral - new Transition (3421, 3422), // &interca -> &intercal - new Transition (3448, 3449), // &Invisib -> &Invisibl - new Transition (3529, 3530), // &Iti -> &Itil - new Transition (3534, 3535), // &iti -> &itil - new Transition (3549, 3550), // &Ium -> Ï - new Transition (3552, 3553), // &ium -> ï - new Transition (3635, 3636), // &Kcedi -> &Kcedil - new Transition (3641, 3642), // &kcedi -> &kcedil - new Transition (3692, 4348), // &l -> &ll - new Transition (3698, 4346), // &L -> &Ll - new Transition (3737, 3741), // &lang -> &langl - new Transition (3746, 3747), // &Lap -> &Lapl - new Transition (3766, 3779), // &larr -> &larrl - new Transition (3782, 3783), // &larrp -> &larrpl - new Transition (3789, 3790), // &larrt -> &larrtl - new Transition (3796, 3797), // &lAtai -> &lAtail - new Transition (3800, 3801), // &latai -> &latail - new Transition (3831, 3832), // &lbrks -> &lbrksl - new Transition (3851, 3852), // &Lcedi -> &Lcedil - new Transition (3856, 3857), // &lcedi -> &lcedil - new Transition (3859, 3860), // &lcei -> &lceil - new Transition (3903, 3904), // &LeftAng -> &LeftAngl - new Transition (3926, 4019), // &left -> &leftl - new Transition (3950, 3951), // &leftarrowtai -> &leftarrowtail - new Transition (3955, 3956), // &LeftCei -> &LeftCeil - new Transition (3964, 3965), // &LeftDoub -> &LeftDoubl - new Transition (3998, 3999), // &LeftF -> &LeftFl - new Transition (4124, 4125), // &LeftTriang -> &LeftTriangl - new Transition (4135, 4136), // &LeftTriangleEqua -> &LeftTriangleEqual - new Transition (4191, 4192), // &leqs -> &leqsl - new Transition (4243, 4244), // &LessEqua -> &LessEqual - new Transition (4254, 4255), // &LessFu -> &LessFul - new Transition (4255, 4256), // &LessFul -> &LessFull - new Transition (4260, 4261), // &LessFullEqua -> &LessFullEqual - new Transition (4284, 4285), // &LessS -> &LessSl - new Transition (4292, 4293), // &LessSlantEqua -> &LessSlantEqual - new Transition (4296, 4297), // &LessTi -> &LessTil - new Transition (4301, 4307), // &lf -> &lfl - new Transition (4330, 4332), // &lharu -> &lharul - new Transition (4334, 4335), // &lhb -> &lhbl - new Transition (4436, 4447), // &Long -> &Longl - new Transition (4458, 4459), // &long -> &longl - new Transition (4548, 4549), // &looparrow -> &looparrowl - new Transition (4560, 4569), // &lop -> &lopl - new Transition (4623, 4625), // &lpar -> &lparl - new Transition (4698, 4720), // < -> <l - new Transition (4767, 4909), // &m -> &ml - new Transition (4768, 4772), // &ma -> &mal - new Transition (4789, 4796), // &mapsto -> &mapstol - new Transition (4839, 4840), // &measuredang -> &measuredangl - new Transition (4843, 4854), // &Me -> &Mel - new Transition (4854, 4855), // &Mel -> &Mell - new Transition (4904, 4905), // &MinusP -> &MinusPl - new Transition (4917, 4918), // &mnp -> &mnpl - new Transition (4924, 4925), // &mode -> &model - new Transition (4952, 4954), // &mu -> &mul - new Transition (4965, 5256), // &n -> &nl - new Transition (4967, 4968), // &nab -> &nabl - new Transition (5005, 5006), // &natura -> &natural - new Transition (5036, 5037), // &Ncedi -> &Ncedil - new Transition (5041, 5042), // &ncedi -> &ncedil - new Transition (5204, 5205), // &ngeqs -> &ngeqsl - new Transition (5272, 5326), // &nL -> &nLl - new Transition (5316, 5317), // &nleqs -> &nleqsl - new Transition (5399, 5400), // &NotDoub -> &NotDoubl - new Transition (5408, 5409), // &NotDoubleVertica -> &NotDoubleVertical - new Transition (5414, 5415), // &NotE -> &NotEl - new Transition (5424, 5425), // &NotEqua -> &NotEqual - new Transition (5428, 5429), // &NotEqualTi -> &NotEqualTil - new Transition (5450, 5451), // &NotGreaterEqua -> &NotGreaterEqual - new Transition (5454, 5455), // &NotGreaterFu -> &NotGreaterFul - new Transition (5455, 5456), // &NotGreaterFul -> &NotGreaterFull - new Transition (5460, 5461), // &NotGreaterFullEqua -> &NotGreaterFullEqual - new Transition (5476, 5477), // &NotGreaterS -> &NotGreaterSl - new Transition (5484, 5485), // &NotGreaterSlantEqua -> &NotGreaterSlantEqual - new Transition (5488, 5489), // &NotGreaterTi -> &NotGreaterTil - new Transition (5509, 5510), // &NotHumpEqua -> &NotHumpEqual - new Transition (5537, 5538), // &NotLeftTriang -> &NotLeftTriangl - new Transition (5548, 5549), // &NotLeftTriangleEqua -> &NotLeftTriangleEqual - new Transition (5557, 5558), // &NotLessEqua -> &NotLessEqual - new Transition (5573, 5574), // &NotLessS -> &NotLessSl - new Transition (5581, 5582), // &NotLessSlantEqua -> &NotLessSlantEqual - new Transition (5585, 5586), // &NotLessTi -> &NotLessTil - new Transition (5642, 5643), // &NotPrecedesEqua -> &NotPrecedesEqual - new Transition (5645, 5646), // &NotPrecedesS -> &NotPrecedesSl - new Transition (5653, 5654), // &NotPrecedesSlantEqua -> &NotPrecedesSlantEqual - new Transition (5663, 5664), // &NotReverseE -> &NotReverseEl - new Transition (5680, 5681), // &NotRightTriang -> &NotRightTriangl - new Transition (5691, 5692), // &NotRightTriangleEqua -> &NotRightTriangleEqual - new Transition (5710, 5711), // &NotSquareSubsetEqua -> &NotSquareSubsetEqual - new Transition (5723, 5724), // &NotSquareSupersetEqua -> &NotSquareSupersetEqual - new Transition (5735, 5736), // &NotSubsetEqua -> &NotSubsetEqual - new Transition (5748, 5749), // &NotSucceedsEqua -> &NotSucceedsEqual - new Transition (5751, 5752), // &NotSucceedsS -> &NotSucceedsSl - new Transition (5759, 5760), // &NotSucceedsSlantEqua -> &NotSucceedsSlantEqual - new Transition (5763, 5764), // &NotSucceedsTi -> &NotSucceedsTil - new Transition (5778, 5779), // &NotSupersetEqua -> &NotSupersetEqual - new Transition (5782, 5783), // &NotTi -> &NotTil - new Transition (5790, 5791), // &NotTildeEqua -> &NotTildeEqual - new Transition (5794, 5795), // &NotTildeFu -> &NotTildeFul - new Transition (5795, 5796), // &NotTildeFul -> &NotTildeFull - new Transition (5800, 5801), // &NotTildeFullEqua -> &NotTildeFullEqual - new Transition (5804, 5805), // &NotTildeTi -> &NotTildeTil - new Transition (5815, 5816), // &NotVertica -> &NotVertical - new Transition (5825, 5826), // &npara -> &nparal - new Transition (5826, 5827), // &nparal -> &nparall - new Transition (5828, 5829), // &nparalle -> &nparallel - new Transition (5831, 5832), // &npars -> &nparsl - new Transition (5836, 5837), // &npo -> &npol - new Transition (5921, 5922), // &nshortpara -> &nshortparal - new Transition (5922, 5923), // &nshortparal -> &nshortparall - new Transition (5924, 5925), // &nshortparalle -> &nshortparallel - new Transition (5988, 6003), // &nt -> &ntl - new Transition (5989, 5990), // &ntg -> &ntgl - new Transition (5993, 5994), // &Nti -> &Ntil - new Transition (5998, 5999), // &nti -> &ntil - new Transition (6010, 6011), // &ntriang -> &ntriangl - new Transition (6012, 6013), // &ntriangle -> &ntrianglel - new Transition (6043, 6084), // &nv -> &nvl - new Transition (6138, 6238), // &o -> &ol - new Transition (6169, 6170), // &Odb -> &Odbl - new Transition (6174, 6175), // &odb -> &odbl - new Transition (6186, 6187), // &odso -> &odsol - new Transition (6190, 6191), // &OE -> &OEl - new Transition (6195, 6196), // &oe -> &oel - new Transition (6302, 6336), // &op -> &opl - new Transition (6311, 6312), // &OpenCur -> &OpenCurl - new Transition (6317, 6318), // &OpenCurlyDoub -> &OpenCurlyDoubl - new Transition (6368, 6369), // &ors -> &orsl - new Transition (6378, 6386), // &Os -> &Osl - new Transition (6382, 6391), // &os -> &osl - new Transition (6396, 6397), // &oso -> &osol - new Transition (6400, 6401), // &Oti -> &Otil - new Transition (6406, 6407), // &oti -> &otil - new Transition (6423, 6424), // &Oum -> Ö - new Transition (6427, 6428), // &oum -> ö - new Transition (6463, 6555), // &p -> &pl - new Transition (6467, 6469), // ¶ -> ¶l - new Transition (6469, 6470), // ¶l -> ¶ll - new Transition (6471, 6472), // ¶lle -> ¶llel - new Transition (6474, 6478), // &pars -> &parsl - new Transition (6482, 6587), // &P -> &Pl - new Transition (6487, 6488), // &Partia -> &Partial - new Transition (6508, 6509), // &permi -> &permil - new Transition (6616, 6617), // &Poincarep -> &Poincarepl - new Transition (6666, 6667), // &preccur -> &preccurl - new Transition (6682, 6683), // &PrecedesEqua -> &PrecedesEqual - new Transition (6685, 6686), // &PrecedesS -> &PrecedesSl - new Transition (6693, 6694), // &PrecedesSlantEqua -> &PrecedesSlantEqual - new Transition (6697, 6698), // &PrecedesTi -> &PrecedesTil - new Transition (6754, 6760), // &prof -> &profl - new Transition (6755, 6756), // &profa -> &profal - new Transition (6780, 6781), // &Proportiona -> &Proportional - new Transition (6792, 6793), // &prure -> &prurel - new Transition (6876, 7442), // &r -> &rl - new Transition (6912, 6918), // &rang -> &rangl - new Transition (6932, 6950), // &rarr -> &rarrl - new Transition (6953, 6954), // &rarrp -> &rarrpl - new Transition (6960, 6961), // &Rarrt -> &Rarrtl - new Transition (6963, 6964), // &rarrt -> &rarrtl - new Transition (6970, 6971), // &rAtai -> &rAtail - new Transition (6975, 6976), // &ratai -> &ratail - new Transition (6982, 6983), // &rationa -> &rational - new Transition (7015, 7016), // &rbrks -> &rbrksl - new Transition (7035, 7036), // &Rcedi -> &Rcedil - new Transition (7040, 7041), // &rcedi -> &rcedil - new Transition (7043, 7044), // &rcei -> &rceil - new Transition (7053, 7057), // &rd -> &rdl - new Transition (7075, 7076), // &rea -> &real - new Transition (7102, 7103), // &ReverseE -> &ReverseEl - new Transition (7112, 7113), // &ReverseEqui -> &ReverseEquil - new Transition (7126, 7127), // &ReverseUpEqui -> &ReverseUpEquil - new Transition (7135, 7141), // &rf -> &rfl - new Transition (7160, 7162), // &rharu -> &rharul - new Transition (7177, 7178), // &RightAng -> &RightAngl - new Transition (7202, 7294), // &right -> &rightl - new Transition (7225, 7226), // &rightarrowtai -> &rightarrowtail - new Transition (7230, 7231), // &RightCei -> &RightCeil - new Transition (7239, 7240), // &RightDoub -> &RightDoubl - new Transition (7273, 7274), // &RightF -> &RightFl - new Transition (7369, 7370), // &RightTriang -> &RightTriangl - new Transition (7380, 7381), // &RightTriangleEqua -> &RightTriangleEqual - new Transition (7481, 7491), // &rop -> &ropl - new Transition (7506, 7507), // &RoundImp -> &RoundImpl - new Transition (7520, 7521), // &rppo -> &rppol - new Transition (7579, 7585), // &rtri -> &rtril - new Transition (7590, 7591), // &Ru -> &Rul - new Transition (7594, 7595), // &RuleDe -> &RuleDel - new Transition (7601, 7602), // &ru -> &rul - new Transition (7617, 7878), // &s -> &sl - new Transition (7655, 7656), // &Scedi -> &Scedil - new Transition (7659, 7660), // &scedi -> &scedil - new Transition (7681, 7682), // &scpo -> &scpol - new Transition (7806, 7807), // &shortpara -> &shortparal - new Transition (7807, 7808), // &shortparal -> &shortparall - new Transition (7809, 7810), // &shortparalle -> &shortparallel - new Transition (7847, 7861), // &sim -> &siml - new Transition (7868, 7869), // &simp -> &simpl - new Transition (7884, 7885), // &Sma -> &Smal - new Transition (7885, 7886), // &Smal -> &Small - new Transition (7890, 7891), // &SmallCirc -> &SmallCircl - new Transition (7895, 7896), // &sma -> &smal - new Transition (7896, 7897), // &smal -> &small - new Transition (7915, 7916), // &smepars -> &smeparsl - new Transition (7918, 7921), // &smi -> &smil - new Transition (7936, 7942), // &so -> &sol - new Transition (8042, 8043), // &SquareSubsetEqua -> &SquareSubsetEqual - new Transition (8055, 8056), // &SquareSupersetEqua -> &SquareSupersetEqual - new Transition (8087, 8088), // &ssmi -> &ssmil - new Transition (8115, 8116), // &straightepsi -> &straightepsil - new Transition (8146, 8147), // &submu -> &submul - new Transition (8155, 8156), // &subp -> &subpl - new Transition (8181, 8182), // &SubsetEqua -> &SubsetEqual - new Transition (8210, 8211), // &succcur -> &succcurl - new Transition (8226, 8227), // &SucceedsEqua -> &SucceedsEqual - new Transition (8229, 8230), // &SucceedsS -> &SucceedsSl - new Transition (8237, 8238), // &SucceedsSlantEqua -> &SucceedsSlantEqual - new Transition (8241, 8242), // &SucceedsTi -> &SucceedsTil - new Transition (8284, 8328), // &sup -> &supl - new Transition (8317, 8318), // &SupersetEqua -> &SupersetEqual - new Transition (8322, 8323), // &suphso -> &suphsol - new Transition (8334, 8335), // &supmu -> &supmul - new Transition (8343, 8344), // &supp -> &suppl - new Transition (8395, 8396), // &sz -> &szl - new Transition (8433, 8434), // &Tcedi -> &Tcedil - new Transition (8438, 8439), // &tcedi -> &tcedil - new Transition (8449, 8450), // &te -> &tel - new Transition (8544, 8545), // &Ti -> &Til - new Transition (8549, 8550), // &ti -> &til - new Transition (8557, 8558), // &TildeEqua -> &TildeEqual - new Transition (8561, 8562), // &TildeFu -> &TildeFul - new Transition (8562, 8563), // &TildeFul -> &TildeFull - new Transition (8567, 8568), // &TildeFullEqua -> &TildeFullEqual - new Transition (8571, 8572), // &TildeTi -> &TildeTil - new Transition (8636, 8637), // &triang -> &triangl - new Transition (8638, 8645), // &triangle -> &trianglel - new Transition (8678, 8679), // &Trip -> &Tripl - new Transition (8685, 8686), // &trip -> &tripl - new Transition (8746, 8747), // &twohead -> &twoheadl - new Transition (8775, 8887), // &u -> &ul - new Transition (8835, 8836), // &Udb -> &Udbl - new Transition (8840, 8841), // &udb -> &udbl - new Transition (8878, 8879), // &uhar -> &uharl - new Transition (8883, 8884), // &uhb -> &uhbl - new Transition (8909, 8914), // &um -> ¨ - new Transition (8949, 8950), // &UnionP -> &UnionPl - new Transition (8983, 9064), // &up -> &upl - new Transition (9037, 9038), // &UpEqui -> &UpEquil - new Transition (9052, 9053), // &upharpoon -> &upharpoonl - new Transition (9093, 9100), // &Upsi -> &Upsil - new Transition (9096, 9104), // &upsi -> &upsil - new Transition (9167, 9168), // &Uti -> &Util - new Transition (9172, 9173), // &uti -> &util - new Transition (9188, 9189), // &Uum -> Ü - new Transition (9191, 9192), // &uum -> ü - new Transition (9197, 9198), // &uwang -> &uwangl - new Transition (9201, 9420), // &v -> &vl - new Transition (9212, 9213), // &varepsi -> &varepsil - new Transition (9289, 9290), // &vartriang -> &vartriangl - new Transition (9291, 9292), // &vartriangle -> &vartrianglel - new Transition (9328, 9340), // &Vdash -> &Vdashl - new Transition (9345, 9355), // &ve -> &vel - new Transition (9355, 9356), // &vel -> &vell - new Transition (9376, 9377), // &Vertica -> &Vertical - new Transition (9398, 9399), // &VerticalTi -> &VerticalTil - new Transition (9548, 9585), // &x -> &xl - new Transition (9611, 9614), // &xop -> &xopl - new Transition (9646, 9647), // &xup -> &xupl - new Transition (9741, 9742), // &Yum -> &Yuml - new Transition (9744, 9745) // &yum -> ÿ - }; - TransitionTable_m = new Transition[177] { - new Transition (0, 4767), // & -> &m - new Transition (1, 98), // &A -> &Am - new Transition (8, 103), // &a -> &am - new Transition (83, 84), // &alefsy -> &alefsym - new Transition (136, 143), // &ang -> &angm - new Transition (262, 263), // &asy -> &asym - new Transition (281, 282), // &Au -> &Aum - new Transition (285, 286), // &au -> &aum - new Transition (320, 321), // &backpri -> &backprim - new Transition (325, 326), // &backsi -> &backsim - new Transition (384, 399), // &be -> &bem - new Transition (466, 467), // &bigoti -> &bigotim - new Transition (605, 606), // &botto -> &bottom - new Transition (613, 656), // &box -> &boxm - new Transition (668, 669), // &boxti -> &boxtim - new Transition (721, 722), // &bpri -> &bprim - new Transition (748, 749), // &bse -> &bsem - new Transition (752, 753), // &bsi -> &bsim - new Transition (767, 774), // &bu -> &bum - new Transition (781, 782), // &Bu -> &Bum - new Transition (904, 905), // &ccupss -> &ccupssm - new Transition (915, 927), // &ce -> &cem - new Transition (966, 968), // &check -> &checkm - new Transition (979, 1059), // &cir -> &cirm - new Transition (1044, 1045), // &CircleTi -> &CircleTim - new Transition (1131, 1142), // &co -> &com - new Transition (1142, 1143), // &com -> &comm - new Transition (1154, 1155), // &comple -> &complem - new Transition (1349, 1351), // &curarr -> &curarrm - new Transition (1516, 1529), // &de -> &dem - new Transition (1558, 1603), // &Dia -> &Diam - new Transition (1600, 1601), // &dia -> &diam - new Transition (1634, 1635), // &diga -> &digam - new Transition (1635, 1636), // &digam -> &digamm - new Transition (1652, 1653), // ÷onti -> ÷ontim - new Transition (1694, 1713), // &dot -> &dotm - new Transition (2108, 2228), // &E -> &Em - new Transition (2115, 2233), // &e -> &em - new Transition (2207, 2208), // &Ele -> &Elem - new Transition (2249, 2250), // &EmptyS -> &EmptySm - new Transition (2267, 2268), // &EmptyVeryS -> &EmptyVerySm - new Transition (2351, 2352), // &eqsi -> &eqsim - new Transition (2393, 2394), // &Equilibriu -> &Equilibrium - new Transition (2430, 2431), // &Esi -> &Esim - new Transition (2433, 2434), // &esi -> &esim - new Transition (2447, 2448), // &Eu -> &Eum - new Transition (2451, 2452), // &eu -> &eum - new Transition (2524, 2525), // &fe -> &fem - new Transition (2559, 2560), // &FilledS -> &FilledSm - new Transition (2575, 2576), // &FilledVeryS -> &FilledVerySm - new Transition (2702, 2714), // &ga -> &gam - new Transition (2709, 2710), // &Ga -> &Gam - new Transition (2710, 2711), // &Gam -> &Gamm - new Transition (2714, 2715), // &gam -> &gamm - new Transition (2811, 2812), // &gi -> &gim - new Transition (2850, 2851), // &gnsi -> &gnsim - new Transition (2931, 2932), // &gsi -> &gsim - new Transition (2999, 3000), // >rsi -> >rsim - new Transition (3021, 3030), // &ha -> &ham - new Transition (3126, 3131), // &ho -> &hom - new Transition (3207, 3208), // &Hu -> &Hum - new Transition (3215, 3216), // &HumpDownHu -> &HumpDownHum - new Transition (3236, 3330), // &I -> &Im - new Transition (3243, 3336), // &i -> &im - new Transition (3452, 3453), // &InvisibleCo -> &InvisibleCom - new Transition (3453, 3454), // &InvisibleCom -> &InvisibleComm - new Transition (3458, 3459), // &InvisibleTi -> &InvisibleTim - new Transition (3539, 3549), // &Iu -> &Ium - new Transition (3544, 3552), // &iu -> &ium - new Transition (3561, 3577), // &j -> &jm - new Transition (3692, 4385), // &l -> &lm - new Transition (3698, 4379), // &L -> &Lm - new Transition (3699, 3723), // &La -> &Lam - new Transition (3705, 3728), // &la -> &lam - new Transition (3711, 3712), // &lae -> &laem - new Transition (3786, 3787), // &larrsi -> &larrsim - new Transition (4115, 4116), // &leftthreeti -> &leftthreetim - new Transition (4281, 4282), // &lesssi -> &lesssim - new Transition (4419, 4420), // &lnsi -> &lnsim - new Transition (4458, 4502), // &long -> &longm - new Transition (4574, 4575), // &loti -> &lotim - new Transition (4628, 4646), // &lr -> &lrm - new Transition (4669, 4670), // &lsi -> &lsim - new Transition (4715, 4716), // <i -> <im - new Transition (4810, 4811), // &mco -> &mcom - new Transition (4811, 4812), // &mcom -> &mcomm - new Transition (4846, 4847), // &Mediu -> &Medium - new Transition (4952, 4961), // &mu -> &mum - new Transition (4956, 4957), // &multi -> &multim - new Transition (4965, 5343), // &n -> &nm - new Transition (5014, 5015), // &nbu -> &nbum - new Transition (5095, 5096), // &NegativeMediu -> &NegativeMedium - new Transition (5145, 5146), // &nesi -> &nesim - new Transition (5216, 5217), // &ngsi -> &ngsim - new Transition (5329, 5330), // &nlsi -> &nlsim - new Transition (5416, 5417), // &NotEle -> &NotElem - new Transition (5494, 5495), // &NotHu -> &NotHum - new Transition (5502, 5503), // &NotHumpDownHu -> &NotHumpDownHum - new Transition (5665, 5666), // &NotReverseEle -> &NotReverseElem - new Transition (5895, 5934), // &ns -> &nsm - new Transition (5913, 5914), // &nshort -> &nshortm - new Transition (5927, 5928), // &nsi -> &nsim - new Transition (6032, 6034), // &nu -> &num - new Transition (6108, 6109), // &nvsi -> &nvsim - new Transition (6131, 6258), // &O -> &Om - new Transition (6138, 6263), // &o -> &om - new Transition (6227, 6232), // &oh -> &ohm - new Transition (6348, 6358), // &ord -> º - new Transition (6400, 6411), // &Oti -> &Otim - new Transition (6406, 6415), // &oti -> &otim - new Transition (6422, 6423), // &Ou -> &Oum - new Transition (6426, 6427), // &ou -> &oum - new Transition (6463, 6607), // &p -> &pm - new Transition (6475, 6476), // &parsi -> &parsim - new Transition (6498, 6507), // &per -> &perm - new Transition (6527, 6532), // &ph -> &phm - new Transition (6532, 6533), // &phm -> &phmm - new Transition (6567, 6596), // &plus -> &plusm - new Transition (6600, 6601), // &plussi -> &plussim - new Transition (6718, 6719), // &precnsi -> &precnsim - new Transition (6722, 6723), // &precsi -> &precsim - new Transition (6725, 6726), // &Pri -> &Prim - new Transition (6729, 6730), // &pri -> &prim - new Transition (6742, 6743), // &prnsi -> &prnsim - new Transition (6787, 6788), // &prsi -> &prsim - new Transition (6835, 6836), // &qpri -> &qprim - new Transition (6876, 7453), // &r -> &rm - new Transition (6901, 6902), // &rae -> &raem - new Transition (6957, 6958), // &rarrsi -> &rarrsim - new Transition (7104, 7105), // &ReverseEle -> &ReverseElem - new Transition (7118, 7119), // &ReverseEquilibriu -> &ReverseEquilibrium - new Transition (7132, 7133), // &ReverseUpEquilibriu -> &ReverseUpEquilibrium - new Transition (7360, 7361), // &rightthreeti -> &rightthreetim - new Transition (7442, 7451), // &rl -> &rlm - new Transition (7464, 7465), // &rn -> &rnm - new Transition (7496, 7497), // &roti -> &rotim - new Transition (7504, 7505), // &RoundI -> &RoundIm - new Transition (7573, 7574), // &rti -> &rtim - new Transition (7610, 7883), // &S -> &Sm - new Transition (7617, 7894), // &s -> &sm - new Transition (7677, 7678), // &scnsi -> &scnsim - new Transition (7688, 7689), // &scsi -> &scsim - new Transition (7703, 7721), // &se -> &sem - new Transition (7729, 7730), // &set -> &setm - new Transition (7798, 7799), // &short -> &shortm - new Transition (7834, 7835), // &Sig -> &Sigm - new Transition (7838, 7847), // &si -> &sim - new Transition (7839, 7840), // &sig -> &sigm - new Transition (7900, 7901), // &smallset -> &smallsetm - new Transition (8077, 8086), // &ss -> &ssm - new Transition (8082, 8083), // &sset -> &ssetm - new Transition (8127, 8275), // &Su -> &Sum - new Transition (8130, 8277), // &su -> &sum - new Transition (8131, 8145), // &sub -> &subm - new Transition (8190, 8191), // &subsi -> &subsim - new Transition (8262, 8263), // &succnsi -> &succnsim - new Transition (8266, 8267), // &succsi -> &succsim - new Transition (8284, 8333), // &sup -> &supm - new Transition (8367, 8368), // &supsi -> &supsim - new Transition (8488, 8489), // &thetasy -> &thetasym - new Transition (8504, 8505), // &thicksi -> &thicksim - new Transition (8532, 8533), // &thksi -> &thksim - new Transition (8549, 8576), // &ti -> &tim - new Transition (8619, 8620), // &tpri -> &tprim - new Transition (8633, 8670), // &tri -> &trim - new Transition (8694, 8695), // &triti -> &tritim - new Transition (8702, 8703), // &trpeziu -> &trpezium - new Transition (8768, 8904), // &U -> &Um - new Transition (8775, 8909), // &u -> &um - new Transition (9043, 9044), // &UpEquilibriu -> &UpEquilibrium - new Transition (9182, 9191), // &uu -> &uum - new Transition (9187, 9188), // &Uu -> &Uum - new Transition (9254, 9255), // &varsig -> &varsigm - new Transition (9548, 9594), // &x -> &xm - new Transition (9619, 9620), // &xoti -> &xotim - new Transition (9736, 9744), // &yu -> &yum - new Transition (9740, 9741) // &Yu -> &Yum - }; - TransitionTable_n = new Transition[303] { - new Transition (0, 4965), // & -> &n - new Transition (1, 116), // &A -> &An - new Transition (8, 119), // &a -> &an - new Transition (122, 123), // &anda -> &andan - new Transition (185, 186), // &Aogo -> &Aogon - new Transition (190, 191), // &aogo -> &aogon - new Transition (221, 222), // &ApplyFu -> &ApplyFun - new Transition (226, 227), // &ApplyFunctio -> &ApplyFunction - new Transition (238, 239), // &Ari -> &Arin - new Transition (243, 244), // &ari -> &arin - new Transition (257, 258), // &Assig -> &Assign - new Transition (291, 292), // &awco -> &awcon - new Transition (293, 294), // &awconi -> &awconin - new Transition (297, 298), // &awi -> &awin - new Transition (301, 579), // &b -> &bn - new Transition (306, 307), // &backco -> &backcon - new Transition (315, 316), // &backepsilo -> &backepsilon - new Transition (370, 371), // &bco -> &bcon - new Transition (409, 410), // &ber -> &bern - new Transition (414, 415), // &Ber -> &Bern - new Transition (433, 434), // &betwee -> &between - new Transition (484, 485), // &bigtria -> &bigtrian - new Transition (491, 492), // &bigtriangledow -> &bigtriangledown - new Transition (520, 563), // &bla -> &blan - new Transition (526, 527), // &blackloze -> &blacklozen - new Transition (541, 542), // &blacktria -> &blacktrian - new Transition (549, 550), // &blacktriangledow -> &blacktriangledown - new Transition (657, 658), // &boxmi -> &boxmin - new Transition (807, 808), // &capa -> &capan - new Transition (838, 839), // &CapitalDiffere -> &CapitalDifferen - new Transition (852, 853), // &caro -> &caron - new Transition (869, 870), // &Ccaro -> &Ccaron - new Transition (873, 874), // &ccaro -> &ccaron - new Transition (894, 895), // &Cco -> &Ccon - new Transition (896, 897), // &Cconi -> &Cconin - new Transition (915, 933), // &ce -> &cen - new Transition (920, 936), // &Ce -> &Cen - new Transition (1033, 1034), // &CircleMi -> &CircleMin - new Transition (1053, 1054), // &cirf -> &cirfn - new Transition (1055, 1056), // &cirfni -> &cirfnin - new Transition (1077, 1078), // &ClockwiseCo -> &ClockwiseCon - new Transition (1083, 1084), // &ClockwiseContourI -> &ClockwiseContourIn - new Transition (1126, 1171), // &Co -> &Con - new Transition (1128, 1129), // &Colo -> &Colon - new Transition (1131, 1164), // &co -> &con - new Transition (1133, 1134), // &colo -> &colon - new Transition (1150, 1151), // &compf -> &compfn - new Transition (1156, 1157), // &compleme -> &complemen - new Transition (1175, 1176), // &Congrue -> &Congruen - new Transition (1179, 1180), // &Coni -> &Conin - new Transition (1183, 1184), // &coni -> &conin - new Transition (1191, 1192), // &ContourI -> &ContourIn - new Transition (1226, 1227), // &Cou -> &Coun - new Transition (1241, 1242), // &CounterClockwiseCo -> &CounterClockwiseCon - new Transition (1247, 1248), // &CounterClockwiseContourI -> &CounterClockwiseContourIn - new Transition (1378, 1379), // &curre -> ¤ - new Transition (1409, 1410), // &cwco -> &cwcon - new Transition (1411, 1412), // &cwconi -> &cwconin - new Transition (1415, 1416), // &cwi -> &cwin - new Transition (1477, 1478), // &Dcaro -> &Dcaron - new Transition (1483, 1484), // &dcaro -> &dcaron - new Transition (1604, 1605), // &Diamo -> &Diamon - new Transition (1608, 1609), // &diamo -> &diamon - new Transition (1625, 1626), // &Differe -> &Differen - new Transition (1640, 1641), // &disi -> &disin - new Transition (1649, 1650), // ÷o -> ÷on - new Transition (1657, 1658), // &divo -> &divon - new Transition (1672, 1673), // &dlcor -> &dlcorn - new Transition (1714, 1715), // &dotmi -> &dotmin - new Transition (1749, 1750), // &DoubleCo -> &DoubleCon - new Transition (1755, 1756), // &DoubleContourI -> &DoubleContourIn - new Transition (1768, 1769), // &DoubleDow -> &DoubleDown - new Transition (1801, 1802), // &DoubleLo -> &DoubleLon - new Transition (1861, 1862), // &DoubleUpDow -> &DoubleUpDown - new Transition (1881, 1882), // &Dow -> &Down - new Transition (1895, 1896), // &dow -> &down - new Transition (1923, 1924), // &downdow -> &downdown - new Transition (1937, 1938), // &downharpoo -> &downharpoon - new Transition (2033, 2034), // &drcor -> &drcorn - new Transition (2087, 2088), // &dwa -> &dwan - new Transition (2115, 2290), // &e -> &en - new Transition (2130, 2131), // &Ecaro -> &Ecaron - new Transition (2136, 2137), // &ecaro -> &ecaron - new Transition (2150, 2151), // &ecolo -> &ecolon - new Transition (2209, 2210), // &Eleme -> &Elemen - new Transition (2213, 2214), // &eli -> &elin - new Transition (2298, 2299), // &Eogo -> &Eogon - new Transition (2303, 2304), // &eogo -> &eogon - new Transition (2330, 2331), // &Epsilo -> &Epsilon - new Transition (2334, 2335), // &epsilo -> &epsilon - new Transition (2347, 2348), // &eqcolo -> &eqcolon - new Transition (2355, 2356), // &eqsla -> &eqslan - new Transition (2479, 2480), // &expectatio -> &expectation - new Transition (2483, 2484), // &Expo -> &Expon - new Transition (2485, 2486), // &Expone -> &Exponen - new Transition (2493, 2494), // &expo -> &expon - new Transition (2495, 2496), // &expone -> &exponen - new Transition (2503, 2604), // &f -> &fn - new Transition (2507, 2508), // &falli -> &fallin - new Transition (2600, 2601), // &flt -> &fltn - new Transition (2643, 2644), // &fparti -> &fpartin - new Transition (2690, 2691), // &frow -> &frown - new Transition (2701, 2832), // &g -> &gn - new Transition (2777, 2778), // &geqsla -> &geqslan - new Transition (2908, 2909), // &GreaterSla -> &GreaterSlan - new Transition (3002, 3011), // &gv -> &gvn - new Transition (3005, 3006), // &gvert -> &gvertn - new Transition (3091, 3092), // &herco -> &hercon - new Transition (3174, 3175), // &Horizo -> &Horizon - new Transition (3180, 3181), // &HorizontalLi -> &HorizontalLin - new Transition (3212, 3213), // &HumpDow -> &HumpDown - new Transition (3233, 3234), // &hyphe -> &hyphen - new Transition (3236, 3398), // &I -> &In - new Transition (3243, 3378), // &i -> &in - new Transition (3301, 3311), // &ii -> &iin - new Transition (3303, 3308), // &iii -> &iiin - new Transition (3304, 3305), // &iiii -> &iiiin - new Transition (3313, 3314), // &iinfi -> &iinfin - new Transition (3345, 3346), // &Imagi -> &Imagin - new Transition (3353, 3354), // &imagli -> &imaglin - new Transition (3386, 3387), // &infi -> &infin - new Transition (3430, 3431), // &Intersectio -> &Intersection - new Transition (3473, 3474), // &Iogo -> &Iogon - new Transition (3477, 3478), // &iogo -> &iogon - new Transition (3511, 3512), // &isi -> &isin - new Transition (3657, 3658), // &kgree -> &kgreen - new Transition (3692, 4401), // &l -> &ln - new Transition (3699, 3733), // &La -> &Lan - new Transition (3705, 3736), // &la -> &lan - new Transition (3720, 3721), // &lagra -> &lagran - new Transition (3840, 3841), // &Lcaro -> &Lcaron - new Transition (3846, 3847), // &lcaro -> &lcaron - new Transition (3901, 3902), // &LeftA -> &LeftAn - new Transition (3957, 3958), // &LeftCeili -> &LeftCeilin - new Transition (3975, 3976), // &LeftDow -> &LeftDown - new Transition (4009, 4010), // &leftharpoo -> &leftharpoon - new Transition (4013, 4014), // &leftharpoondow -> &leftharpoondown - new Transition (4070, 4071), // &leftrightharpoo -> &leftrightharpoon - new Transition (4122, 4123), // &LeftTria -> &LeftTrian - new Transition (4142, 4143), // &LeftUpDow -> &LeftUpDown - new Transition (4193, 4194), // &leqsla -> &leqslan - new Transition (4286, 4287), // &LessSla -> &LessSlan - new Transition (4356, 4357), // &llcor -> &llcorn - new Transition (4422, 4457), // &lo -> &lon - new Transition (4423, 4424), // &loa -> &loan - new Transition (4434, 4435), // &Lo -> &Lon - new Transition (4614, 4615), // &loze -> &lozen - new Transition (4635, 4636), // &lrcor -> &lrcorn - new Transition (4755, 4764), // &lv -> &lvn - new Transition (4758, 4759), // &lvert -> &lvertn - new Transition (4767, 4916), // &m -> &mn - new Transition (4793, 4794), // &mapstodow -> &mapstodown - new Transition (4837, 4838), // &measureda -> &measuredan - new Transition (4856, 4857), // &Melli -> &Mellin - new Transition (4871, 4890), // &mi -> &min - new Transition (4900, 4901), // &Mi -> &Min - new Transition (4966, 4983), // &na -> &nan - new Transition (5027, 5028), // &Ncaro -> &Ncaron - new Transition (5031, 5032), // &ncaro -> &ncaron - new Transition (5044, 5045), // &nco -> &ncon - new Transition (5105, 5114), // &NegativeThi -> &NegativeThin - new Transition (5127, 5128), // &NegativeVeryThi -> &NegativeVeryThin - new Transition (5178, 5179), // &NewLi -> &NewLin - new Transition (5206, 5207), // &ngeqsla -> &ngeqslan - new Transition (5318, 5319), // &nleqsla -> &nleqslan - new Transition (5347, 5354), // &No -> &Non - new Transition (5360, 5361), // &NonBreaki -> &NonBreakin - new Transition (5378, 5620), // ¬ -> ¬n - new Transition (5381, 5382), // &NotCo -> &NotCon - new Transition (5386, 5387), // &NotCongrue -> &NotCongruen - new Transition (5418, 5419), // &NotEleme -> &NotElemen - new Transition (5478, 5479), // &NotGreaterSla -> &NotGreaterSlan - new Transition (5499, 5500), // &NotHumpDow -> &NotHumpDown - new Transition (5512, 5513), // ¬i -> ¬in - new Transition (5535, 5536), // &NotLeftTria -> &NotLeftTrian - new Transition (5575, 5576), // &NotLessSla -> &NotLessSlan - new Transition (5647, 5648), // &NotPrecedesSla -> &NotPrecedesSlan - new Transition (5667, 5668), // &NotReverseEleme -> &NotReverseElemen - new Transition (5678, 5679), // &NotRightTria -> &NotRightTrian - new Transition (5753, 5754), // &NotSucceedsSla -> &NotSucceedsSlan - new Transition (5838, 5839), // &npoli -> &npolin - new Transition (6008, 6009), // &ntria -> &ntrian - new Transition (6078, 6079), // &nvi -> &nvin - new Transition (6081, 6082), // &nvinfi -> &nvinfin - new Transition (6111, 6126), // &nw -> &nwn - new Transition (6211, 6212), // &ogo -> &ogon - new Transition (6234, 6235), // &oi -> &oin - new Transition (6252, 6253), // &oli -> &olin - new Transition (6279, 6280), // &Omicro -> &Omicron - new Transition (6282, 6290), // &omi -> &omin - new Transition (6285, 6286), // &omicro -> &omicron - new Transition (6307, 6308), // &Ope -> &Open - new Transition (6454, 6455), // &OverPare -> &OverParen - new Transition (6499, 6500), // &perc -> &percn - new Transition (6514, 6515), // &perte -> &perten - new Transition (6537, 6538), // &pho -> &phon - new Transition (6556, 6557), // &pla -> &plan - new Transition (6591, 6592), // &PlusMi -> &PlusMin - new Transition (6596, 6597), // &plusm -> ± - new Transition (6610, 6611), // &Poi -> &Poin - new Transition (6618, 6619), // &Poincarepla -> &Poincareplan - new Transition (6623, 6624), // &poi -> &poin - new Transition (6626, 6627), // &pointi -> &pointin - new Transition (6636, 6637), // &pou -> &poun - new Transition (6642, 6735), // &pr -> &prn - new Transition (6655, 6705), // &prec -> &precn - new Transition (6687, 6688), // &PrecedesSla -> &PrecedesSlan - new Transition (6761, 6762), // &profli -> &proflin - new Transition (6777, 6778), // &Proportio -> &Proportion - new Transition (6807, 6808), // &pu -> &pun - new Transition (6821, 6822), // &qi -> &qin - new Transition (6851, 6852), // &quater -> &quatern - new Transition (6854, 6855), // &quaternio -> &quaternion - new Transition (6858, 6859), // &quati -> &quatin - new Transition (6876, 7464), // &r -> &rn - new Transition (6882, 6911), // &ra -> &ran - new Transition (6887, 6908), // &Ra -> &Ran - new Transition (6979, 6981), // &ratio -> &ration - new Transition (7024, 7025), // &Rcaro -> &Rcaron - new Transition (7030, 7031), // &rcaro -> &rcaron - new Transition (7078, 7079), // &reali -> &realin - new Transition (7106, 7107), // &ReverseEleme -> &ReverseElemen - new Transition (7175, 7176), // &RightA -> &RightAn - new Transition (7199, 7428), // &ri -> &rin - new Transition (7232, 7233), // &RightCeili -> &RightCeilin - new Transition (7250, 7251), // &RightDow -> &RightDown - new Transition (7284, 7285), // &rightharpoo -> &rightharpoon - new Transition (7288, 7289), // &rightharpoondow -> &rightharpoondown - new Transition (7310, 7311), // &rightleftharpoo -> &rightleftharpoon - new Transition (7367, 7368), // &RightTria -> &RightTrian - new Transition (7387, 7388), // &RightUpDow -> &RightUpDown - new Transition (7432, 7433), // &risi -> &risin - new Transition (7470, 7471), // &roa -> &roan - new Transition (7501, 7502), // &Rou -> &Roun - new Transition (7522, 7523), // &rppoli -> &rppolin - new Transition (7631, 7670), // &sc -> &scn - new Transition (7638, 7639), // &Scaro -> &Scaron - new Transition (7642, 7643), // &scaro -> &scaron - new Transition (7683, 7684), // &scpoli -> &scpolin - new Transition (7730, 7736), // &setm -> &setmn - new Transition (7731, 7732), // &setmi -> &setmin - new Transition (7748, 7749), // &sfrow -> &sfrown - new Transition (7778, 7779), // &ShortDow -> &ShortDown - new Transition (7847, 7865), // &sim -> &simn - new Transition (7902, 7903), // &smallsetmi -> &smallsetmin - new Transition (8019, 8020), // &SquareI -> &SquareIn - new Transition (8029, 8030), // &SquareIntersectio -> &SquareIntersection - new Transition (8058, 8059), // &SquareU -> &SquareUn - new Transition (8061, 8062), // &SquareUnio -> &SquareUnion - new Transition (8083, 8084), // &ssetm -> &ssetmn - new Transition (8106, 8124), // &str -> &strn - new Transition (8117, 8118), // &straightepsilo -> &straightepsilon - new Transition (8130, 8279), // &su -> &sun - new Transition (8131, 8150), // &sub -> &subn - new Transition (8171, 8184), // &subset -> &subsetn - new Transition (8199, 8249), // &succ -> &succn - new Transition (8231, 8232), // &SucceedsSla -> &SucceedsSlan - new Transition (8284, 8338), // &sup -> &supn - new Transition (8354, 8361), // &supset -> &supsetn - new Transition (8375, 8390), // &sw -> &swn - new Transition (8422, 8423), // &Tcaro -> &Tcaron - new Transition (8428, 8429), // &tcaro -> &tcaron - new Transition (8493, 8516), // &thi -> &thin - new Transition (8507, 8520), // &Thi -> &Thin - new Transition (8541, 8542), // &thor -> þ - new Transition (8549, 8587), // &ti -> &tin - new Transition (8634, 8635), // &tria -> &trian - new Transition (8642, 8643), // &triangledow -> &triangledown - new Transition (8671, 8672), // &trimi -> &trimin - new Transition (8768, 8916), // &U -> &Un - new Transition (8890, 8891), // &ulcor -> &ulcorn - new Transition (8936, 8937), // &UnderPare -> &UnderParen - new Transition (8946, 8947), // &Unio -> &Union - new Transition (8956, 8957), // &Uogo -> &Uogon - new Transition (8961, 8962), // &uogo -> &uogon - new Transition (8996, 8997), // &UpArrowDow -> &UpArrowDown - new Transition (9006, 9007), // &UpDow -> &UpDown - new Transition (9016, 9017), // &Updow -> &Updown - new Transition (9026, 9027), // &updow -> &updown - new Transition (9051, 9052), // &upharpoo -> &upharpoon - new Transition (9101, 9102), // &Upsilo -> &Upsilon - new Transition (9105, 9106), // &upsilo -> &upsilon - new Transition (9130, 9131), // &urcor -> &urcorn - new Transition (9141, 9142), // &Uri -> &Urin - new Transition (9145, 9146), // &uri -> &urin - new Transition (9195, 9196), // &uwa -> &uwan - new Transition (9201, 9425), // &v -> &vn - new Transition (9202, 9203), // &va -> &van - new Transition (9208, 9223), // &var -> &varn - new Transition (9214, 9215), // &varepsilo -> &varepsilon - new Transition (9227, 9228), // &varnothi -> &varnothin - new Transition (9262, 9263), // &varsubset -> &varsubsetn - new Transition (9272, 9273), // &varsupset -> &varsupsetn - new Transition (9287, 9288), // &vartria -> &vartrian - new Transition (9383, 9384), // &VerticalLi -> &VerticalLin - new Transition (9406, 9407), // &VeryThi -> &VeryThin - new Transition (9459, 9460), // &vsub -> &vsubn - new Transition (9465, 9466), // &vsup -> &vsupn - new Transition (9548, 9598), // &x -> &xn - new Transition (9699, 9700), // &ye -> ¥ - new Transition (9764, 9765), // &Zcaro -> &Zcaron - new Transition (9770, 9771), // &zcaro -> &zcaron - new Transition (9848, 9851) // &zw -> &zwn - }; - TransitionTable_o = new Transition[460] { - new Transition (0, 6138), // & -> &o - new Transition (1, 183), // &A -> &Ao - new Transition (8, 188), // &a -> &ao - new Transition (129, 130), // &andsl -> &andslo - new Transition (184, 185), // &Aog -> &Aogo - new Transition (189, 190), // &aog -> &aogo - new Transition (199, 213), // &ap -> &apo - new Transition (225, 226), // &ApplyFuncti -> &ApplyFunctio - new Transition (230, 231), // &appr -> &appro - new Transition (290, 291), // &awc -> &awco - new Transition (301, 598), // &b -> &bo - new Transition (305, 306), // &backc -> &backco - new Transition (314, 315), // &backepsil -> &backepsilo - new Transition (331, 594), // &B -> &Bo - new Transition (369, 370), // &bc -> &bco - new Transition (381, 382), // &bdqu -> &bdquo - new Transition (410, 411), // &bern -> &berno - new Transition (415, 416), // &Bern -> &Berno - new Transition (443, 455), // &big -> &bigo - new Transition (456, 457), // &bigod -> &bigodo - new Transition (489, 490), // &bigtriangled -> &bigtriangledo - new Transition (515, 516), // &bkar -> &bkaro - new Transition (519, 575), // &bl -> &blo - new Transition (523, 524), // &blackl -> &blacklo - new Transition (547, 548), // &blacktriangled -> &blacktriangledo - new Transition (579, 591), // &bn -> &bno - new Transition (587, 588), // &bN -> &bNo - new Transition (604, 605), // &bott -> &botto - new Transition (614, 615), // &boxb -> &boxbo - new Transition (744, 757), // &bs -> &bso - new Transition (789, 1126), // &C -> &Co - new Transition (796, 1131), // &c -> &co - new Transition (824, 825), // &capd -> &capdo - new Transition (848, 852), // &car -> &caro - new Transition (866, 894), // &Cc -> &Cco - new Transition (868, 869), // &Ccar -> &Ccaro - new Transition (872, 873), // &ccar -> &ccaro - new Transition (907, 908), // &Cd -> &Cdo - new Transition (911, 912), // &cd -> &cdo - new Transition (940, 941), // &CenterD -> &CenterDo - new Transition (946, 947), // ¢erd -> ¢erdo - new Transition (990, 991), // &circlearr -> &circlearro - new Transition (1024, 1025), // &CircleD -> &CircleDo - new Transition (1068, 1069), // &Cl -> &Clo - new Transition (1076, 1077), // &ClockwiseC -> &ClockwiseCo - new Transition (1079, 1080), // &ClockwiseCont -> &ClockwiseConto - new Transition (1099, 1100), // &CloseCurlyD -> &CloseCurlyDo - new Transition (1106, 1107), // &CloseCurlyDoubleQu -> &CloseCurlyDoubleQuo - new Transition (1112, 1113), // &CloseCurlyQu -> &CloseCurlyQuo - new Transition (1127, 1128), // &Col -> &Colo - new Transition (1132, 1133), // &col -> &colo - new Transition (1167, 1168), // &congd -> &congdo - new Transition (1187, 1188), // &Cont -> &Conto - new Transition (1206, 1207), // &copr -> &copro - new Transition (1210, 1211), // &Copr -> &Copro - new Transition (1232, 1233), // &CounterCl -> &CounterClo - new Transition (1240, 1241), // &CounterClockwiseC -> &CounterClockwiseCo - new Transition (1243, 1244), // &CounterClockwiseCont -> &CounterClockwiseConto - new Transition (1256, 1266), // &cr -> &cro - new Transition (1261, 1262), // &Cr -> &Cro - new Transition (1288, 1289), // &ctd -> &ctdo - new Transition (1318, 1341), // &cup -> &cupo - new Transition (1337, 1338), // &cupd -> &cupdo - new Transition (1385, 1386), // &curvearr -> &curvearro - new Transition (1408, 1409), // &cwc -> &cwco - new Transition (1425, 1685), // &D -> &Do - new Transition (1432, 1679), // &d -> &do - new Transition (1466, 1467), // &dbkar -> &dbkaro - new Transition (1476, 1477), // &Dcar -> &Dcaro - new Transition (1482, 1483), // &dcar -> &dcaro - new Transition (1490, 1503), // &DD -> &DDo - new Transition (1492, 1510), // &dd -> &ddo - new Transition (1573, 1574), // &DiacriticalD -> &DiacriticalDo - new Transition (1601, 1608), // &diam -> &diamo - new Transition (1603, 1604), // &Diam -> &Diamo - new Transition (1643, 1657), // &div -> &divo - new Transition (1647, 1649), // ÷ -> ÷o - new Transition (1670, 1671), // &dlc -> &dlco - new Transition (1675, 1676), // &dlcr -> &dlcro - new Transition (1696, 1697), // &DotD -> &DotDo - new Transition (1703, 1704), // &doteqd -> &doteqdo - new Transition (1748, 1749), // &DoubleC -> &DoubleCo - new Transition (1751, 1752), // &DoubleCont -> &DoubleConto - new Transition (1764, 1765), // &DoubleD -> &DoubleDo - new Transition (1772, 1773), // &DoubleDownArr -> &DoubleDownArro - new Transition (1776, 1801), // &DoubleL -> &DoubleLo - new Transition (1782, 1783), // &DoubleLeftArr -> &DoubleLeftArro - new Transition (1793, 1794), // &DoubleLeftRightArr -> &DoubleLeftRightArro - new Transition (1810, 1811), // &DoubleLongLeftArr -> &DoubleLongLeftArro - new Transition (1821, 1822), // &DoubleLongLeftRightArr -> &DoubleLongLeftRightArro - new Transition (1832, 1833), // &DoubleLongRightArr -> &DoubleLongRightArro - new Transition (1843, 1844), // &DoubleRightArr -> &DoubleRightArro - new Transition (1855, 1856), // &DoubleUpArr -> &DoubleUpArro - new Transition (1859, 1860), // &DoubleUpD -> &DoubleUpDo - new Transition (1865, 1866), // &DoubleUpDownArr -> &DoubleUpDownArro - new Transition (1885, 1886), // &DownArr -> &DownArro - new Transition (1891, 1892), // &Downarr -> &Downarro - new Transition (1899, 1900), // &downarr -> &downarro - new Transition (1911, 1912), // &DownArrowUpArr -> &DownArrowUpArro - new Transition (1921, 1922), // &downd -> &downdo - new Transition (1927, 1928), // &downdownarr -> &downdownarro - new Transition (1935, 1936), // &downharp -> &downharpo - new Transition (1936, 1937), // &downharpo -> &downharpoo - new Transition (1962, 1963), // &DownLeftRightVect -> &DownLeftRightVecto - new Transition (1972, 1973), // &DownLeftTeeVect -> &DownLeftTeeVecto - new Transition (1979, 1980), // &DownLeftVect -> &DownLeftVecto - new Transition (1998, 1999), // &DownRightTeeVect -> &DownRightTeeVecto - new Transition (2005, 2006), // &DownRightVect -> &DownRightVecto - new Transition (2019, 2020), // &DownTeeArr -> &DownTeeArro - new Transition (2027, 2028), // &drbkar -> &drbkaro - new Transition (2031, 2032), // &drc -> &drco - new Transition (2036, 2037), // &drcr -> &drcro - new Transition (2044, 2054), // &ds -> &dso - new Transition (2058, 2059), // &Dstr -> &Dstro - new Transition (2063, 2064), // &dstr -> &dstro - new Transition (2068, 2069), // &dtd -> &dtdo - new Transition (2108, 2296), // &E -> &Eo - new Transition (2115, 2301), // &e -> &eo - new Transition (2129, 2130), // &Ecar -> &Ecaro - new Transition (2133, 2148), // &ec -> &eco - new Transition (2135, 2136), // &ecar -> &ecaro - new Transition (2149, 2150), // &ecol -> &ecolo - new Transition (2157, 2166), // &eD -> &eDo - new Transition (2158, 2159), // &eDD -> &eDDo - new Transition (2162, 2163), // &Ed -> &Edo - new Transition (2169, 2170), // &ed -> &edo - new Transition (2176, 2177), // &efD -> &efDo - new Transition (2200, 2201), // &egsd -> &egsdo - new Transition (2224, 2225), // &elsd -> &elsdo - new Transition (2297, 2298), // &Eog -> &Eogo - new Transition (2302, 2303), // &eog -> &eogo - new Transition (2329, 2330), // &Epsil -> &Epsilo - new Transition (2333, 2334), // &epsil -> &epsilo - new Transition (2340, 2345), // &eqc -> &eqco - new Transition (2346, 2347), // &eqcol -> &eqcolo - new Transition (2414, 2415), // &erD -> &erDo - new Transition (2426, 2427), // &esd -> &esdo - new Transition (2455, 2456), // &eur -> &euro - new Transition (2472, 2493), // &exp -> &expo - new Transition (2478, 2479), // &expectati -> &expectatio - new Transition (2482, 2483), // &Exp -> &Expo - new Transition (2503, 2612), // &f -> &fo - new Transition (2510, 2511), // &fallingd -> &fallingdo - new Transition (2517, 2608), // &F -> &Fo - new Transition (2604, 2605), // &fn -> &fno - new Transition (2647, 2689), // &fr -> &fro - new Transition (2701, 2857), // &g -> &go - new Transition (2708, 2853), // &G -> &Go - new Transition (2755, 2756), // &Gd -> &Gdo - new Transition (2759, 2760), // &gd -> &gdo - new Transition (2786, 2787), // &gesd -> &gesdo - new Transition (2788, 2790), // &gesdot -> &gesdoto - new Transition (2837, 2838), // &gnappr -> &gnappro - new Transition (2950, 2951), // >d -> >do - new Transition (2969, 2970), // >rappr -> >rappro - new Transition (2976, 2977), // >rd -> >rdo - new Transition (3014, 3159), // &H -> &Ho - new Transition (3020, 3126), // &h -> &ho - new Transition (3090, 3091), // &herc -> &herco - new Transition (3116, 3117), // &hksear -> &hksearo - new Transition (3122, 3123), // &hkswar -> &hkswaro - new Transition (3126, 3136), // &ho -> &hoo - new Transition (3144, 3145), // &hookleftarr -> &hookleftarro - new Transition (3155, 3156), // &hookrightarr -> &hookrightarro - new Transition (3173, 3174), // &Horiz -> &Horizo - new Transition (3198, 3199), // &Hstr -> &Hstro - new Transition (3203, 3204), // &hstr -> &hstro - new Transition (3210, 3211), // &HumpD -> &HumpDo - new Transition (3236, 3471), // &I -> &Io - new Transition (3243, 3467), // &i -> &io - new Transition (3265, 3266), // &Id -> &Ido - new Transition (3301, 3316), // &ii -> &iio - new Transition (3336, 3365), // &im -> &imo - new Transition (3378, 3393), // &in -> &ino - new Transition (3394, 3395), // &inod -> &inodo - new Transition (3429, 3430), // &Intersecti -> &Intersectio - new Transition (3440, 3441), // &intpr -> &intpro - new Transition (3451, 3452), // &InvisibleC -> &InvisibleCo - new Transition (3472, 3473), // &Iog -> &Iogo - new Transition (3476, 3477), // &iog -> &iogo - new Transition (3493, 3494), // &ipr -> &ipro - new Transition (3514, 3515), // &isind -> &isindo - new Transition (3555, 3582), // &J -> &Jo - new Transition (3561, 3586), // &j -> &jo - new Transition (3618, 3676), // &K -> &Ko - new Transition (3624, 3680), // &k -> &ko - new Transition (3692, 4422), // &l -> &lo - new Transition (3698, 4434), // &L -> &Lo - new Transition (3756, 3757), // &laqu -> « - new Transition (3839, 3840), // &Lcar -> &Lcaro - new Transition (3845, 3846), // &lcar -> &lcaro - new Transition (3874, 3875), // &ldqu -> &ldquo - new Transition (3915, 3916), // &LeftArr -> &LeftArro - new Transition (3921, 3922), // &Leftarr -> &Leftarro - new Transition (3929, 3930), // &leftarr -> &leftarro - new Transition (3944, 3945), // &LeftArrowRightArr -> &LeftArrowRightArro - new Transition (3961, 3962), // &LeftD -> &LeftDo - new Transition (3983, 3984), // &LeftDownTeeVect -> &LeftDownTeeVecto - new Transition (3990, 3991), // &LeftDownVect -> &LeftDownVecto - new Transition (3999, 4000), // &LeftFl -> &LeftFlo - new Transition (4000, 4001), // &LeftFlo -> &LeftFloo - new Transition (4007, 4008), // &leftharp -> &leftharpo - new Transition (4008, 4009), // &leftharpo -> &leftharpoo - new Transition (4011, 4012), // &leftharpoond -> &leftharpoondo - new Transition (4025, 4026), // &leftleftarr -> &leftleftarro - new Transition (4037, 4038), // &LeftRightArr -> &LeftRightArro - new Transition (4048, 4049), // &Leftrightarr -> &Leftrightarro - new Transition (4059, 4060), // &leftrightarr -> &leftrightarro - new Transition (4068, 4069), // &leftrightharp -> &leftrightharpo - new Transition (4069, 4070), // &leftrightharpo -> &leftrightharpoo - new Transition (4081, 4082), // &leftrightsquigarr -> &leftrightsquigarro - new Transition (4088, 4089), // &LeftRightVect -> &LeftRightVecto - new Transition (4098, 4099), // &LeftTeeArr -> &LeftTeeArro - new Transition (4105, 4106), // &LeftTeeVect -> &LeftTeeVecto - new Transition (4140, 4141), // &LeftUpD -> &LeftUpDo - new Transition (4147, 4148), // &LeftUpDownVect -> &LeftUpDownVecto - new Transition (4157, 4158), // &LeftUpTeeVect -> &LeftUpTeeVecto - new Transition (4164, 4165), // &LeftUpVect -> &LeftUpVecto - new Transition (4175, 4176), // &LeftVect -> &LeftVecto - new Transition (4202, 4203), // &lesd -> &lesdo - new Transition (4204, 4206), // &lesdot -> &lesdoto - new Transition (4219, 4220), // &lessappr -> &lessappro - new Transition (4223, 4224), // &lessd -> &lessdo - new Transition (4307, 4308), // &lfl -> &lflo - new Transition (4308, 4309), // &lflo -> &lfloo - new Transition (4354, 4355), // &llc -> &llco - new Transition (4366, 4367), // &Lleftarr -> &Lleftarro - new Transition (4381, 4382), // &Lmid -> &Lmido - new Transition (4385, 4391), // &lm -> &lmo - new Transition (4387, 4388), // &lmid -> &lmido - new Transition (4406, 4407), // &lnappr -> &lnappro - new Transition (4422, 4542), // &lo -> &loo - new Transition (4443, 4444), // &LongLeftArr -> &LongLeftArro - new Transition (4453, 4454), // &Longleftarr -> &Longleftarro - new Transition (4465, 4466), // &longleftarr -> &longleftarro - new Transition (4476, 4477), // &LongLeftRightArr -> &LongLeftRightArro - new Transition (4487, 4488), // &Longleftrightarr -> &Longleftrightarro - new Transition (4498, 4499), // &longleftrightarr -> &longleftrightarro - new Transition (4506, 4507), // &longmapst -> &longmapsto - new Transition (4516, 4517), // &LongRightArr -> &LongRightArro - new Transition (4527, 4528), // &Longrightarr -> &Longrightarro - new Transition (4538, 4539), // &longrightarr -> &longrightarro - new Transition (4546, 4547), // &looparr -> &looparro - new Transition (4597, 4598), // &LowerLeftArr -> &LowerLeftArro - new Transition (4608, 4609), // &LowerRightArr -> &LowerRightArro - new Transition (4633, 4634), // &lrc -> &lrco - new Transition (4655, 4656), // &lsaqu -> &lsaquo - new Transition (4679, 4680), // &lsqu -> &lsquo - new Transition (4685, 4686), // &Lstr -> &Lstro - new Transition (4690, 4691), // &lstr -> &lstro - new Transition (4706, 4707), // <d -> <do - new Transition (4767, 4922), // &m -> &mo - new Transition (4781, 4928), // &M -> &Mo - new Transition (4788, 4789), // &mapst -> &mapsto - new Transition (4791, 4792), // &mapstod -> &mapstodo - new Transition (4809, 4810), // &mc -> &mco - new Transition (4826, 4827), // &mDD -> &mDDo - new Transition (4868, 4869), // &mh -> &mho - new Transition (4873, 4874), // &micr -> µ - new Transition (4886, 4887), // &midd -> &middo - new Transition (4946, 4947), // &mstp -> &mstpo - new Transition (4965, 5372), // &n -> &no - new Transition (4971, 5347), // &N -> &No - new Transition (4986, 4993), // &nap -> &napo - new Transition (4997, 4998), // &nappr -> &nappro - new Transition (5020, 5044), // &nc -> &nco - new Transition (5026, 5027), // &Ncar -> &Ncaro - new Transition (5030, 5031), // &ncar -> &ncaro - new Transition (5048, 5049), // &ncongd -> &ncongdo - new Transition (5075, 5077), // &nearr -> &nearro - new Transition (5080, 5081), // &ned -> &nedo - new Transition (5278, 5279), // &nLeftarr -> &nLeftarro - new Transition (5286, 5287), // &nleftarr -> &nleftarro - new Transition (5297, 5298), // &nLeftrightarr -> &nLeftrightarro - new Transition (5308, 5309), // &nleftrightarr -> &nleftrightarro - new Transition (5380, 5381), // &NotC -> &NotCo - new Transition (5396, 5397), // &NotD -> &NotDo - new Transition (5497, 5498), // &NotHumpD -> &NotHumpDo - new Transition (5515, 5516), // ¬ind -> ¬indo - new Transition (5821, 5836), // &np -> &npo - new Transition (5875, 5876), // &nRightarr -> &nRightarro - new Transition (5885, 5886), // &nrightarr -> &nrightarro - new Transition (5910, 5911), // &nsh -> &nsho - new Transition (6037, 6038), // &numer -> &numero - new Transition (6121, 6123), // &nwarr -> &nwarro - new Transition (6131, 6294), // &O -> &Oo - new Transition (6138, 6298), // &o -> &oo - new Transition (6163, 6182), // &od -> &odo - new Transition (6185, 6186), // &ods -> &odso - new Transition (6210, 6211), // &og -> &ogo - new Transition (6247, 6248), // &olcr -> &olcro - new Transition (6278, 6279), // &Omicr -> &Omicro - new Transition (6284, 6285), // &omicr -> &omicro - new Transition (6314, 6315), // &OpenCurlyD -> &OpenCurlyDo - new Transition (6321, 6322), // &OpenCurlyDoubleQu -> &OpenCurlyDoubleQuo - new Transition (6327, 6328), // &OpenCurlyQu -> &OpenCurlyQuo - new Transition (6342, 6365), // &or -> &oro - new Transition (6351, 6353), // &order -> &ordero - new Transition (6361, 6362), // &orig -> &origo - new Transition (6369, 6370), // &orsl -> &orslo - new Transition (6382, 6396), // &os -> &oso - new Transition (6463, 6622), // &p -> &po - new Transition (6482, 6609), // &P -> &Po - new Transition (6503, 6504), // &peri -> &perio - new Transition (6527, 6537), // &ph -> &pho - new Transition (6548, 6549), // &pitchf -> &pitchfo - new Transition (6580, 6581), // &plusd -> &plusdo - new Transition (6604, 6605), // &plustw -> &plustwo - new Transition (6640, 6748), // &Pr -> &Pro - new Transition (6642, 6745), // &pr -> &pro - new Transition (6660, 6661), // &precappr -> &precappro - new Transition (6709, 6710), // &precnappr -> &precnappro - new Transition (6772, 6773), // &Prop -> &Propo - new Transition (6776, 6777), // &Proporti -> &Proportio - new Transition (6783, 6784), // &propt -> &propto - new Transition (6813, 6825), // &Q -> &Qo - new Transition (6817, 6829), // &q -> &qo - new Transition (6847, 6873), // &qu -> &quo - new Transition (6853, 6854), // &quaterni -> &quaternio - new Transition (6876, 7469), // &r -> &ro - new Transition (6886, 7485), // &R -> &Ro - new Transition (6922, 6923), // &raqu -> » - new Transition (6978, 6979), // &rati -> &ratio - new Transition (7023, 7024), // &Rcar -> &Rcaro - new Transition (7029, 7030), // &rcar -> &rcaro - new Transition (7064, 7065), // &rdqu -> &rdquo - new Transition (7141, 7142), // &rfl -> &rflo - new Transition (7142, 7143), // &rflo -> &rfloo - new Transition (7155, 7167), // &rh -> &rho - new Transition (7164, 7165), // &Rh -> &Rho - new Transition (7189, 7190), // &RightArr -> &RightArro - new Transition (7195, 7196), // &Rightarr -> &Rightarro - new Transition (7205, 7206), // &rightarr -> &rightarro - new Transition (7219, 7220), // &RightArrowLeftArr -> &RightArrowLeftArro - new Transition (7236, 7237), // &RightD -> &RightDo - new Transition (7258, 7259), // &RightDownTeeVect -> &RightDownTeeVecto - new Transition (7265, 7266), // &RightDownVect -> &RightDownVecto - new Transition (7274, 7275), // &RightFl -> &RightFlo - new Transition (7275, 7276), // &RightFlo -> &RightFloo - new Transition (7282, 7283), // &rightharp -> &rightharpo - new Transition (7283, 7284), // &rightharpo -> &rightharpoo - new Transition (7286, 7287), // &rightharpoond -> &rightharpoondo - new Transition (7300, 7301), // &rightleftarr -> &rightleftarro - new Transition (7308, 7309), // &rightleftharp -> &rightleftharpo - new Transition (7309, 7310), // &rightleftharpo -> &rightleftharpoo - new Transition (7321, 7322), // &rightrightarr -> &rightrightarro - new Transition (7333, 7334), // &rightsquigarr -> &rightsquigarro - new Transition (7343, 7344), // &RightTeeArr -> &RightTeeArro - new Transition (7350, 7351), // &RightTeeVect -> &RightTeeVecto - new Transition (7385, 7386), // &RightUpD -> &RightUpDo - new Transition (7392, 7393), // &RightUpDownVect -> &RightUpDownVecto - new Transition (7402, 7403), // &RightUpTeeVect -> &RightUpTeeVecto - new Transition (7409, 7410), // &RightUpVect -> &RightUpVecto - new Transition (7420, 7421), // &RightVect -> &RightVecto - new Transition (7435, 7436), // &risingd -> &risingdo - new Transition (7453, 7454), // &rm -> &rmo - new Transition (7519, 7520), // &rpp -> &rppo - new Transition (7538, 7539), // &Rrightarr -> &Rrightarro - new Transition (7545, 7546), // &rsaqu -> &rsaquo - new Transition (7562, 7563), // &rsqu -> &rsquo - new Transition (7610, 7949), // &S -> &So - new Transition (7617, 7936), // &s -> &so - new Transition (7626, 7627), // &sbqu -> &sbquo - new Transition (7637, 7638), // &Scar -> &Scaro - new Transition (7641, 7642), // &scar -> &scaro - new Transition (7680, 7681), // &scp -> &scpo - new Transition (7695, 7696), // &sd -> &sdo - new Transition (7713, 7715), // &searr -> &searro - new Transition (7745, 7747), // &sfr -> &sfro - new Transition (7751, 7796), // &sh -> &sho - new Transition (7772, 7773), // &Sh -> &Sho - new Transition (7776, 7777), // &ShortD -> &ShortDo - new Transition (7782, 7783), // &ShortDownArr -> &ShortDownArro - new Transition (7792, 7793), // &ShortLeftArr -> &ShortLeftArro - new Transition (7819, 7820), // &ShortRightArr -> &ShortRightArro - new Transition (7827, 7828), // &ShortUpArr -> &ShortUpArro - new Transition (7849, 7850), // &simd -> &simdo - new Transition (8028, 8029), // &SquareIntersecti -> &SquareIntersectio - new Transition (8060, 8061), // &SquareUni -> &SquareUnio - new Transition (8116, 8117), // &straightepsil -> &straightepsilo - new Transition (8133, 8134), // &subd -> &subdo - new Transition (8141, 8142), // &subed -> &subedo - new Transition (8204, 8205), // &succappr -> &succappro - new Transition (8253, 8254), // &succnappr -> &succnappro - new Transition (8292, 8293), // &supd -> &supdo - new Transition (8304, 8305), // &suped -> &supedo - new Transition (8321, 8322), // &suphs -> &suphso - new Transition (8385, 8387), // &swarr -> &swarro - new Transition (8400, 8604), // &T -> &To - new Transition (8404, 8590), // &t -> &to - new Transition (8421, 8422), // &Tcar -> &Tcaro - new Transition (8427, 8428), // &tcar -> &tcaro - new Transition (8445, 8446), // &td -> &tdo - new Transition (8461, 8540), // &th -> &tho - new Transition (8471, 8472), // &Theref -> &Therefo - new Transition (8476, 8477), // &theref -> &therefo - new Transition (8499, 8500), // &thickappr -> &thickappro - new Transition (8596, 8597), // &topb -> &topbo - new Transition (8608, 8610), // &topf -> &topfo - new Transition (8640, 8641), // &triangled -> &triangledo - new Transition (8664, 8665), // &trid -> &trido - new Transition (8681, 8682), // &TripleD -> &TripleDo - new Transition (8728, 8729), // &Tstr -> &Tstro - new Transition (8733, 8734), // &tstr -> &tstro - new Transition (8737, 8742), // &tw -> &two - new Transition (8753, 8754), // &twoheadleftarr -> &twoheadleftarro - new Transition (8764, 8765), // &twoheadrightarr -> &twoheadrightarro - new Transition (8768, 8954), // &U -> &Uo - new Transition (8775, 8959), // &u -> &uo - new Transition (8783, 8792), // &Uarr -> &Uarro - new Transition (8888, 8889), // &ulc -> &ulco - new Transition (8896, 8897), // &ulcr -> &ulcro - new Transition (8945, 8946), // &Uni -> &Unio - new Transition (8955, 8956), // &Uog -> &Uogo - new Transition (8960, 8961), // &uog -> &uogo - new Transition (8973, 8974), // &UpArr -> &UpArro - new Transition (8979, 8980), // &Uparr -> &Uparro - new Transition (8986, 8987), // &uparr -> &uparro - new Transition (8994, 8995), // &UpArrowD -> &UpArrowDo - new Transition (9000, 9001), // &UpArrowDownArr -> &UpArrowDownArro - new Transition (9004, 9005), // &UpD -> &UpDo - new Transition (9010, 9011), // &UpDownArr -> &UpDownArro - new Transition (9014, 9015), // &Upd -> &Updo - new Transition (9020, 9021), // &Updownarr -> &Updownarro - new Transition (9024, 9025), // &upd -> &updo - new Transition (9030, 9031), // &updownarr -> &updownarro - new Transition (9049, 9050), // &upharp -> &upharpo - new Transition (9050, 9051), // &upharpo -> &upharpoo - new Transition (9077, 9078), // &UpperLeftArr -> &UpperLeftArro - new Transition (9088, 9089), // &UpperRightArr -> &UpperRightArro - new Transition (9100, 9101), // &Upsil -> &Upsilo - new Transition (9104, 9105), // &upsil -> &upsilo - new Transition (9114, 9115), // &UpTeeArr -> &UpTeeArro - new Transition (9122, 9123), // &upuparr -> &upuparro - new Transition (9128, 9129), // &urc -> &urco - new Transition (9136, 9137), // &urcr -> &urcro - new Transition (9162, 9163), // &utd -> &utdo - new Transition (9201, 9436), // &v -> &vo - new Transition (9213, 9214), // &varepsil -> &varepsilo - new Transition (9223, 9224), // &varn -> &varno - new Transition (9237, 9238), // &varpr -> &varpro - new Transition (9240, 9241), // &varpropt -> &varpropto - new Transition (9249, 9250), // &varrh -> &varrho - new Transition (9303, 9432), // &V -> &Vo - new Transition (9393, 9394), // &VerticalSeparat -> &VerticalSeparato - new Transition (9441, 9442), // &vpr -> &vpro - new Transition (9484, 9523), // &W -> &Wo - new Transition (9490, 9527), // &w -> &wo - new Transition (9548, 9602), // &x -> &xo - new Transition (9565, 9607), // &X -> &Xo - new Transition (9603, 9604), // &xod -> &xodo - new Transition (9665, 9716), // &Y -> &Yo - new Transition (9672, 9720), // &y -> &yo - new Transition (9747, 9832), // &Z -> &Zo - new Transition (9754, 9836), // &z -> &zo - new Transition (9763, 9764), // &Zcar -> &Zcaro - new Transition (9769, 9770), // &zcar -> &zcaro - new Transition (9777, 9778), // &Zd -> &Zdo - new Transition (9781, 9782), // &zd -> &zdo - new Transition (9792, 9793) // &Zer -> &Zero - }; - TransitionTable_p = new Transition[278] { - new Transition (0, 6463), // & -> &p - new Transition (1, 216), // &A -> &Ap - new Transition (8, 199), // &a -> &ap - new Transition (79, 94), // &al -> &alp - new Transition (80, 86), // &ale -> &alep - new Transition (89, 90), // &Al -> &Alp - new Transition (103, 114), // &am -> & - new Transition (130, 131), // &andslo -> &andslop - new Transition (172, 173), // &angs -> &angsp - new Transition (183, 193), // &Ao -> &Aop - new Transition (188, 196), // &ao -> &aop - new Transition (199, 229), // &ap -> &app - new Transition (216, 217), // &Ap -> &App - new Transition (263, 264), // &asym -> &asymp - new Transition (301, 719), // &b -> &bp - new Transition (304, 318), // &back -> &backp - new Transition (310, 311), // &backe -> &backep - new Transition (384, 405), // &be -> &bep - new Transition (399, 400), // &bem -> &bemp - new Transition (445, 446), // &bigca -> &bigcap - new Transition (452, 453), // &bigcu -> &bigcup - new Transition (455, 460), // &bigo -> &bigop - new Transition (474, 475), // &bigsqcu -> &bigsqcup - new Transition (494, 495), // &bigtriangleu -> &bigtriangleup - new Transition (497, 498), // &bigu -> &bigup - new Transition (594, 595), // &Bo -> &Bop - new Transition (598, 599), // &bo -> &bop - new Transition (613, 662), // &box -> &boxp - new Transition (774, 775), // &bum -> &bump - new Transition (782, 783), // &Bum -> &Bump - new Transition (790, 803), // &Ca -> &Cap - new Transition (797, 805), // &ca -> &cap - new Transition (814, 815), // &capbrcu -> &capbrcup - new Transition (818, 819), // &capca -> &capcap - new Transition (821, 822), // &capcu -> &capcup - new Transition (862, 863), // &cca -> &ccap - new Transition (900, 901), // &ccu -> &ccup - new Transition (927, 928), // &cem -> &cemp - new Transition (1126, 1200), // &Co -> &Cop - new Transition (1131, 1203), // &co -> &cop - new Transition (1142, 1148), // &com -> &comp - new Transition (1278, 1283), // &csu -> &csup - new Transition (1292, 1318), // &cu -> &cup - new Transition (1301, 1302), // &cue -> &cuep - new Transition (1311, 1313), // &cularr -> &cularrp - new Transition (1315, 1316), // &Cu -> &Cup - new Transition (1323, 1324), // &cupbrca -> &cupbrcap - new Transition (1327, 1328), // &CupCa -> &CupCap - new Transition (1331, 1332), // &cupca -> &cupcap - new Transition (1334, 1335), // &cupcu -> &cupcup - new Transition (1356, 1357), // &curlyeq -> &curlyeqp - new Transition (1529, 1530), // &dem -> &demp - new Transition (1676, 1677), // &dlcro -> &dlcrop - new Transition (1679, 1689), // &do -> &dop - new Transition (1685, 1686), // &Do -> &Dop - new Transition (1694, 1719), // &dot -> &dotp - new Transition (1851, 1852), // &DoubleU -> &DoubleUp - new Transition (1907, 1908), // &DownArrowU -> &DownArrowUp - new Transition (1934, 1935), // &downhar -> &downharp - new Transition (2037, 2038), // &drcro -> &drcrop - new Transition (2108, 2326), // &E -> &Ep - new Transition (2115, 2312), // &e -> &ep - new Transition (2228, 2246), // &Em -> &Emp - new Transition (2233, 2238), // &em -> &emp - new Transition (2279, 2280), // &ems -> &emsp - new Transition (2293, 2294), // &ens -> &ensp - new Transition (2296, 2306), // &Eo -> &Eop - new Transition (2301, 2309), // &eo -> &eop - new Transition (2402, 2403), // &eqv -> &eqvp - new Transition (2458, 2472), // &ex -> &exp - new Transition (2466, 2482), // &Ex -> &Exp - new Transition (2503, 2639), // &f -> &fp - new Transition (2608, 2609), // &Fo -> &Fop - new Transition (2612, 2613), // &fo -> &fop - new Transition (2702, 2722), // &ga -> &gap - new Transition (2833, 2834), // &gna -> &gnap - new Transition (2834, 2836), // &gnap -> &gnapp - new Transition (2853, 2854), // &Go -> &Gop - new Transition (2857, 2858), // &go -> &gop - new Transition (2966, 2967), // >ra -> >rap - new Transition (2967, 2968), // >rap -> >rapp - new Transition (3024, 3025), // &hairs -> &hairsp - new Transition (3086, 3087), // &helli -> &hellip - new Transition (3106, 3107), // &HilbertS -> &HilbertSp - new Transition (3126, 3163), // &ho -> &hop - new Transition (3159, 3160), // &Ho -> &Hop - new Transition (3208, 3209), // &Hum -> &Hump - new Transition (3216, 3217), // &HumpDownHum -> &HumpDownHump - new Transition (3225, 3231), // &hy -> &hyp - new Transition (3243, 3492), // &i -> &ip - new Transition (3330, 3372), // &Im -> &Imp - new Transition (3336, 3368), // &im -> &imp - new Transition (3341, 3357), // &imag -> &imagp - new Transition (3401, 3439), // &int -> &intp - new Transition (3467, 3483), // &io -> &iop - new Transition (3471, 3480), // &Io -> &Iop - new Transition (3582, 3583), // &Jo -> &Jop - new Transition (3586, 3587), // &jo -> &jop - new Transition (3619, 3620), // &Ka -> &Kap - new Transition (3620, 3621), // &Kap -> &Kapp - new Transition (3625, 3626), // &ka -> &kap - new Transition (3626, 3627), // &kap -> &kapp - new Transition (3676, 3677), // &Ko -> &Kop - new Transition (3680, 3681), // &ko -> &kop - new Transition (3692, 4621), // &l -> &lp - new Transition (3699, 3746), // &La -> &Lap - new Transition (3705, 3744), // &la -> &lap - new Transition (3712, 3713), // &laem -> &laemp - new Transition (3766, 3782), // &larr -> &larrp - new Transition (3779, 3780), // &larrl -> &larrlp - new Transition (4006, 4007), // &lefthar -> &leftharp - new Transition (4016, 4017), // &leftharpoonu -> &leftharpoonup - new Transition (4067, 4068), // &leftrighthar -> &leftrightharp - new Transition (4138, 4139), // &LeftU -> &LeftUp - new Transition (4216, 4217), // &lessa -> &lessap - new Transition (4217, 4218), // &lessap -> &lessapp - new Transition (4402, 4403), // &lna -> &lnap - new Transition (4403, 4405), // &lnap -> &lnapp - new Transition (4422, 4560), // &lo -> &lop - new Transition (4434, 4564), // &Lo -> &Lop - new Transition (4503, 4504), // &longma -> &longmap - new Transition (4542, 4543), // &loo -> &loop - new Transition (4767, 4935), // &m -> &mp - new Transition (4768, 4785), // &ma -> &map - new Transition (4782, 4783), // &Ma -> &Map - new Transition (4801, 4802), // &mapstou -> &mapstoup - new Transition (4848, 4849), // &MediumS -> &MediumSp - new Transition (4910, 4911), // &mlc -> &mlcp - new Transition (4916, 4917), // &mn -> &mnp - new Transition (4922, 4932), // &mo -> &mop - new Transition (4928, 4929), // &Mo -> &Mop - new Transition (4945, 4946), // &mst -> &mstp - new Transition (4958, 4959), // &multima -> &multimap - new Transition (4962, 4963), // &muma -> &mumap - new Transition (4965, 5821), // &n -> &np - new Transition (4966, 4986), // &na -> &nap - new Transition (4986, 4996), // &nap -> &napp - new Transition (5011, 5012), // &nbs ->   - new Transition (5015, 5016), // &nbum -> &nbump - new Transition (5021, 5022), // &nca -> &ncap - new Transition (5052, 5053), // &ncu -> &ncup - new Transition (5097, 5098), // &NegativeMediumS -> &NegativeMediumSp - new Transition (5108, 5109), // &NegativeThickS -> &NegativeThickSp - new Transition (5115, 5116), // &NegativeThinS -> &NegativeThinSp - new Transition (5129, 5130), // &NegativeVeryThinS -> &NegativeVeryThinSp - new Transition (5227, 5236), // &nh -> &nhp - new Transition (5347, 5369), // &No -> &Nop - new Transition (5363, 5364), // &NonBreakingS -> &NonBreakingSp - new Transition (5372, 5373), // &no -> &nop - new Transition (5390, 5391), // &NotCu -> &NotCup - new Transition (5393, 5394), // &NotCupCa -> &NotCupCap - new Transition (5495, 5496), // &NotHum -> &NotHump - new Transition (5503, 5504), // &NotHumpDownHum -> &NotHumpDownHump - new Transition (5701, 5713), // &NotSquareSu -> &NotSquareSup - new Transition (5726, 5768), // &NotSu -> &NotSup - new Transition (5895, 5938), // &ns -> &nsp - new Transition (5913, 5918), // &nshort -> &nshortp - new Transition (5944, 5948), // &nsqsu -> &nsqsup - new Transition (5951, 5973), // &nsu -> &nsup - new Transition (6040, 6041), // &nums -> &numsp - new Transition (6044, 6045), // &nva -> &nvap - new Transition (6131, 6306), // &O -> &Op - new Transition (6138, 6302), // &o -> &op - new Transition (6294, 6295), // &Oo -> &Oop - new Transition (6298, 6299), // &oo -> &oop - new Transition (6333, 6334), // &oper -> &operp - new Transition (6370, 6371), // &orslo -> &orslop - new Transition (6498, 6511), // &per -> &perp - new Transition (6609, 6630), // &Po -> &Pop - new Transition (6615, 6616), // &Poincare -> &Poincarep - new Transition (6622, 6633), // &po -> &pop - new Transition (6644, 6645), // &pra -> &prap - new Transition (6657, 6658), // &preca -> &precap - new Transition (6658, 6659), // &precap -> &precapp - new Transition (6706, 6707), // &precna -> &precnap - new Transition (6707, 6708), // &precnap -> &precnapp - new Transition (6736, 6737), // &prna -> &prnap - new Transition (6745, 6770), // &pro -> &prop - new Transition (6748, 6772), // &Pro -> &Prop - new Transition (6810, 6811), // &puncs -> &puncsp - new Transition (6817, 6833), // &q -> &qp - new Transition (6825, 6826), // &Qo -> &Qop - new Transition (6829, 6830), // &qo -> &qop - new Transition (6876, 7512), // &r -> &rp - new Transition (6902, 6903), // &raem -> &raemp - new Transition (6932, 6953), // &rarr -> &rarrp - new Transition (6934, 6935), // &rarra -> &rarrap - new Transition (6950, 6951), // &rarrl -> &rarrlp - new Transition (7076, 7082), // &real -> &realp - new Transition (7121, 7122), // &ReverseU -> &ReverseUp - new Transition (7281, 7282), // &righthar -> &rightharp - new Transition (7291, 7292), // &rightharpoonu -> &rightharpoonup - new Transition (7307, 7308), // &rightlefthar -> &rightleftharp - new Transition (7383, 7384), // &RightU -> &RightUp - new Transition (7469, 7481), // &ro -> &rop - new Transition (7485, 7486), // &Ro -> &Rop - new Transition (7505, 7506), // &RoundIm -> &RoundImp - new Transition (7512, 7519), // &rp -> &rpp - new Transition (7617, 7956), // &s -> &sp - new Transition (7631, 7680), // &sc -> &scp - new Transition (7633, 7634), // &sca -> &scap - new Transition (7671, 7672), // &scna -> &scnap - new Transition (7753, 7754), // &shar -> &sharp - new Transition (7798, 7803), // &short -> &shortp - new Transition (7823, 7824), // &ShortU -> &ShortUp - new Transition (7847, 7868), // &sim -> &simp - new Transition (7908, 7909), // &smash -> &smashp - new Transition (7911, 7912), // &sme -> &smep - new Transition (7936, 7953), // &so -> &sop - new Transition (7949, 7950), // &So -> &Sop - new Transition (7970, 7971), // &sqca -> &sqcap - new Transition (7975, 7976), // &sqcu -> &sqcup - new Transition (7985, 7997), // &sqsu -> &sqsup - new Transition (8033, 8045), // &SquareSu -> &SquareSup - new Transition (8111, 8120), // &straight -> &straightp - new Transition (8112, 8113), // &straighte -> &straightep - new Transition (8127, 8282), // &Su -> &Sup - new Transition (8130, 8284), // &su -> &sup - new Transition (8131, 8155), // &sub -> &subp - new Transition (8193, 8196), // &subsu -> &subsup - new Transition (8201, 8202), // &succa -> &succap - new Transition (8202, 8203), // &succap -> &succapp - new Transition (8250, 8251), // &succna -> &succnap - new Transition (8251, 8252), // &succnap -> &succnapp - new Transition (8284, 8343), // &sup -> &supp - new Transition (8370, 8373), // &supsu -> &supsup - new Transition (8404, 8617), // &t -> &tp - new Transition (8496, 8497), // &thicka -> &thickap - new Transition (8497, 8498), // &thickap -> &thickapp - new Transition (8510, 8511), // &ThickS -> &ThickSp - new Transition (8517, 8518), // &thins -> &thinsp - new Transition (8521, 8522), // &ThinS -> &ThinSp - new Transition (8528, 8529), // &thka -> &thkap - new Transition (8590, 8594), // &to -> &top - new Transition (8604, 8605), // &To -> &Top - new Transition (8628, 8698), // &tr -> &trp - new Transition (8633, 8685), // &tri -> &trip - new Transition (8677, 8678), // &Tri -> &Trip - new Transition (8768, 8970), // &U -> &Up - new Transition (8775, 8983), // &u -> &up - new Transition (8897, 8898), // &ulcro -> &ulcrop - new Transition (8954, 8964), // &Uo -> &Uop - new Transition (8959, 8967), // &uo -> &uop - new Transition (8970, 9068), // &Up -> &Upp - new Transition (9048, 9049), // &uphar -> &upharp - new Transition (9118, 9119), // &upu -> &upup - new Transition (9137, 9138), // &urcro -> &urcrop - new Transition (9201, 9440), // &v -> &vp - new Transition (9208, 9231), // &var -> &varp - new Transition (9209, 9210), // &vare -> &varep - new Transition (9218, 9219), // &varka -> &varkap - new Transition (9219, 9220), // &varkap -> &varkapp - new Transition (9238, 9239), // &varpro -> &varprop - new Transition (9258, 9269), // &varsu -> &varsup - new Transition (9357, 9358), // &velli -> &vellip - new Transition (9388, 9389), // &VerticalSe -> &VerticalSep - new Transition (9408, 9409), // &VeryThinS -> &VeryThinSp - new Transition (9427, 9430), // &vnsu -> &vnsup - new Transition (9432, 9433), // &Vo -> &Vop - new Transition (9436, 9437), // &vo -> &vop - new Transition (9442, 9443), // &vpro -> &vprop - new Transition (9458, 9465), // &vsu -> &vsup - new Transition (9490, 9531), // &w -> &wp - new Transition (9514, 9515), // &weier -> &weierp - new Transition (9523, 9524), // &Wo -> &Wop - new Transition (9527, 9528), // &wo -> &wop - new Transition (9550, 9551), // &xca -> &xcap - new Transition (9557, 9558), // &xcu -> &xcup - new Transition (9595, 9596), // &xma -> &xmap - new Transition (9602, 9611), // &xo -> &xop - new Transition (9607, 9608), // &Xo -> &Xop - new Transition (9642, 9643), // &xsqcu -> &xsqcup - new Transition (9645, 9646), // &xu -> &xup - new Transition (9716, 9717), // &Yo -> &Yop - new Transition (9720, 9721), // &yo -> &yop - new Transition (9799, 9800), // &ZeroWidthS -> &ZeroWidthSp - new Transition (9832, 9833), // &Zo -> &Zop - new Transition (9836, 9837) // &zo -> &zop - }; - TransitionTable_q = new Transition[144] { - new Transition (0, 6817), // & -> &q - new Transition (234, 235), // &approxe -> &approxeq - new Transition (266, 267), // &asympe -> &asympeq - new Transition (328, 329), // &backsime -> &backsimeq - new Transition (379, 380), // &bd -> &bdq - new Transition (471, 472), // &bigs -> &bigsq - new Transition (531, 532), // &blacks -> &blacksq - new Transition (580, 582), // &bne -> &bneq - new Transition (779, 787), // &bumpe -> &bumpeq - new Transition (784, 785), // &Bumpe -> &Bumpeq - new Transition (983, 984), // &circe -> &circeq - new Transition (1138, 1140), // &colone -> &coloneq - new Transition (1355, 1356), // &curlye -> &curlyeq - new Transition (1513, 1514), // &ddotse -> &ddotseq - new Transition (1700, 1701), // &dote -> &doteq - new Transition (1707, 1708), // &DotE -> &DotEq - new Transition (1724, 1725), // &dots -> &dotsq - new Transition (2108, 2367), // &E -> &Eq - new Transition (2115, 2339), // &e -> &eq - new Transition (2254, 2255), // &EmptySmallS -> &EmptySmallSq - new Transition (2272, 2273), // &EmptyVerySmallS -> &EmptyVerySmallSq - new Transition (2514, 2515), // &fallingdotse -> &fallingdotseq - new Transition (2564, 2565), // &FilledSmallS -> &FilledSmallSq - new Transition (2580, 2581), // &FilledVerySmallS -> &FilledVerySmallSq - new Transition (2765, 2771), // &ge -> &geq - new Transition (2771, 2773), // &geq -> &geqq - new Transition (2843, 2845), // &gne -> &gneq - new Transition (2845, 2847), // &gneq -> &gneqq - new Transition (2872, 2873), // &GreaterE -> &GreaterEq - new Transition (2887, 2888), // &GreaterFullE -> &GreaterFullEq - new Transition (2911, 2912), // &GreaterSlantE -> &GreaterSlantEq - new Transition (2942, 2959), // > -> >q - new Transition (2980, 2981), // >re -> >req - new Transition (2981, 2987), // >req -> >reqq - new Transition (3007, 3008), // &gvertne -> &gvertneq - new Transition (3008, 3009), // &gvertneq -> &gvertneqq - new Transition (3219, 3220), // &HumpE -> &HumpEq - new Transition (3243, 3497), // &i -> &iq - new Transition (3705, 3755), // &la -> &laq - new Transition (3869, 3873), // &ld -> &ldq - new Transition (3896, 4187), // &le -> &leq - new Transition (4074, 4075), // &leftrights -> &leftrightsq - new Transition (4132, 4133), // &LeftTriangleE -> &LeftTriangleEq - new Transition (4187, 4189), // &leq -> &leqq - new Transition (4227, 4228), // &lesse -> &lesseq - new Transition (4228, 4233), // &lesseq -> &lesseqq - new Transition (4240, 4241), // &LessE -> &LessEq - new Transition (4257, 4258), // &LessFullE -> &LessFullEq - new Transition (4289, 4290), // &LessSlantE -> &LessSlantEq - new Transition (4412, 4414), // &lne -> &lneq - new Transition (4414, 4416), // &lneq -> &lneqq - new Transition (4652, 4676), // &ls -> &lsq - new Transition (4653, 4654), // &lsa -> &lsaq - new Transition (4698, 4725), // < -> <q - new Transition (4760, 4761), // &lvertne -> &lvertneq - new Transition (4761, 4762), // &lvertneq -> &lvertneqq - new Transition (5064, 5135), // &ne -> &neq - new Transition (5198, 5200), // &nge -> &ngeq - new Transition (5200, 5202), // &ngeq -> &ngeqq - new Transition (5270, 5312), // &nle -> &nleq - new Transition (5312, 5314), // &nleq -> &nleqq - new Transition (5414, 5422), // &NotE -> &NotEq - new Transition (5447, 5448), // &NotGreaterE -> &NotGreaterEq - new Transition (5457, 5458), // &NotGreaterFullE -> &NotGreaterFullEq - new Transition (5481, 5482), // &NotGreaterSlantE -> &NotGreaterSlantEq - new Transition (5506, 5507), // &NotHumpE -> &NotHumpEq - new Transition (5545, 5546), // &NotLeftTriangleE -> &NotLeftTriangleEq - new Transition (5554, 5555), // &NotLessE -> &NotLessEq - new Transition (5578, 5579), // &NotLessSlantE -> &NotLessSlantEq - new Transition (5639, 5640), // &NotPrecedesE -> &NotPrecedesEq - new Transition (5650, 5651), // &NotPrecedesSlantE -> &NotPrecedesSlantEq - new Transition (5688, 5689), // &NotRightTriangleE -> &NotRightTriangleEq - new Transition (5694, 5695), // &NotS -> &NotSq - new Transition (5707, 5708), // &NotSquareSubsetE -> &NotSquareSubsetEq - new Transition (5720, 5721), // &NotSquareSupersetE -> &NotSquareSupersetEq - new Transition (5732, 5733), // &NotSubsetE -> &NotSubsetEq - new Transition (5745, 5746), // &NotSucceedsE -> &NotSucceedsEq - new Transition (5756, 5757), // &NotSucceedsSlantE -> &NotSucceedsSlantEq - new Transition (5775, 5776), // &NotSupersetE -> &NotSupersetEq - new Transition (5787, 5788), // &NotTildeE -> &NotTildeEq - new Transition (5797, 5798), // &NotTildeFullE -> &NotTildeFullEq - new Transition (5852, 5853), // &nprece -> &npreceq - new Transition (5895, 5942), // &ns -> &nsq - new Transition (5930, 5932), // &nsime -> &nsimeq - new Transition (5962, 5963), // &nsubsete -> &nsubseteq - new Transition (5963, 5965), // &nsubseteq -> &nsubseteqq - new Transition (5970, 5971), // &nsucce -> &nsucceq - new Transition (5983, 5984), // &nsupsete -> &nsupseteq - new Transition (5984, 5986), // &nsupseteq -> &nsupseteqq - new Transition (6018, 6019), // &ntrianglelefte -> &ntrianglelefteq - new Transition (6027, 6028), // &ntrianglerighte -> &ntrianglerighteq - new Transition (6669, 6670), // &preccurlye -> &preccurlyeq - new Transition (6679, 6680), // &PrecedesE -> &PrecedesEq - new Transition (6690, 6691), // &PrecedesSlantE -> &PrecedesSlantEq - new Transition (6702, 6703), // &prece -> &preceq - new Transition (6713, 6714), // &precne -> &precneq - new Transition (6714, 6715), // &precneq -> &precneqq - new Transition (6866, 6867), // &queste -> &questeq - new Transition (6882, 6921), // &ra -> &raq - new Transition (7053, 7063), // &rd -> &rdq - new Transition (7102, 7110), // &ReverseE -> &ReverseEq - new Transition (7123, 7124), // &ReverseUpE -> &ReverseUpEq - new Transition (7326, 7327), // &rights -> &rightsq - new Transition (7377, 7378), // &RightTriangleE -> &RightTriangleEq - new Transition (7439, 7440), // &risingdotse -> &risingdotseq - new Transition (7542, 7559), // &rs -> &rsq - new Transition (7543, 7544), // &rsa -> &rsaq - new Transition (7610, 7980), // &S -> &Sq - new Transition (7617, 7968), // &s -> &sq - new Transition (7624, 7625), // &sb -> &sbq - new Transition (7853, 7855), // &sime -> &simeq - new Transition (7994, 7995), // &sqsubsete -> &sqsubseteq - new Transition (8005, 8006), // &sqsupsete -> &sqsupseteq - new Transition (8039, 8040), // &SquareSubsetE -> &SquareSubsetEq - new Transition (8052, 8053), // &SquareSupersetE -> &SquareSupersetEq - new Transition (8173, 8174), // &subsete -> &subseteq - new Transition (8174, 8176), // &subseteq -> &subseteqq - new Transition (8178, 8179), // &SubsetE -> &SubsetEq - new Transition (8185, 8186), // &subsetne -> &subsetneq - new Transition (8186, 8188), // &subsetneq -> &subsetneqq - new Transition (8213, 8214), // &succcurlye -> &succcurlyeq - new Transition (8223, 8224), // &SucceedsE -> &SucceedsEq - new Transition (8234, 8235), // &SucceedsSlantE -> &SucceedsSlantEq - new Transition (8246, 8247), // &succe -> &succeq - new Transition (8257, 8258), // &succne -> &succneq - new Transition (8258, 8259), // &succneq -> &succneqq - new Transition (8314, 8315), // &SupersetE -> &SupersetEq - new Transition (8356, 8357), // &supsete -> &supseteq - new Transition (8357, 8359), // &supseteq -> &supseteqq - new Transition (8362, 8363), // &supsetne -> &supsetneq - new Transition (8363, 8365), // &supsetneq -> &supsetneqq - new Transition (8554, 8555), // &TildeE -> &TildeEq - new Transition (8564, 8565), // &TildeFullE -> &TildeFullEq - new Transition (8638, 8653), // &triangle -> &triangleq - new Transition (8650, 8651), // &trianglelefte -> &trianglelefteq - new Transition (8661, 8662), // &trianglerighte -> &trianglerighteq - new Transition (9034, 9035), // &UpE -> &UpEq - new Transition (9264, 9265), // &varsubsetne -> &varsubsetneq - new Transition (9265, 9267), // &varsubsetneq -> &varsubsetneqq - new Transition (9274, 9275), // &varsupsetne -> &varsupsetneq - new Transition (9275, 9277), // &varsupsetneq -> &varsupsetneqq - new Transition (9352, 9353), // &veee -> &veeeq - new Transition (9508, 9510), // &wedge -> &wedgeq - new Transition (9636, 9640) // &xs -> &xsq - }; - TransitionTable_r = new Transition[942] { - new Transition (0, 6876), // & -> &r - new Transition (1, 237), // &A -> &Ar - new Transition (8, 242), // &a -> &ar - new Transition (15, 16), // &Ab -> &Abr - new Transition (21, 22), // &ab -> &abr - new Transition (34, 35), // &Aci -> &Acir - new Transition (38, 39), // &aci -> &acir - new Transition (60, 65), // &af -> &afr - new Transition (62, 63), // &Af -> &Afr - new Transition (67, 68), // &Ag -> &Agr - new Transition (73, 74), // &ag -> &agr - new Transition (100, 101), // &Amac -> &Amacr - new Transition (105, 106), // &amac -> &amacr - new Transition (136, 164), // &ang -> &angr - new Transition (179, 180), // &angza -> &angzar - new Transition (180, 181), // &angzar -> &angzarr - new Transition (203, 204), // &apaci -> &apacir - new Transition (229, 230), // &app -> &appr - new Transition (248, 249), // &Asc -> &Ascr - new Transition (252, 253), // &asc -> &ascr - new Transition (301, 730), // &b -> &br - new Transition (302, 344), // &ba -> &bar - new Transition (318, 319), // &backp -> &backpr - new Transition (331, 725), // &B -> &Br - new Transition (332, 341), // &Ba -> &Bar - new Transition (360, 361), // &bb -> &bbr - new Transition (365, 366), // &bbrktb -> &bbrktbr - new Transition (384, 409), // &be -> &ber - new Transition (390, 414), // &Be -> &Ber - new Transition (436, 437), // &Bf -> &Bfr - new Transition (439, 440), // &bf -> &bfr - new Transition (448, 449), // &bigci -> &bigcir - new Transition (478, 479), // &bigsta -> &bigstar - new Transition (481, 482), // &bigt -> &bigtr - new Transition (514, 515), // &bka -> &bkar - new Transition (534, 535), // &blacksqua -> &blacksquar - new Transition (538, 539), // &blackt -> &blacktr - new Transition (545, 557), // &blacktriangle -> &blacktriangler - new Transition (618, 630), // &boxD -> &boxDr - new Transition (623, 634), // &boxd -> &boxdr - new Transition (673, 685), // &boxU -> &boxUr - new Transition (678, 689), // &boxu -> &boxur - new Transition (691, 713), // &boxV -> &boxVr - new Transition (693, 717), // &boxv -> &boxvr - new Transition (719, 720), // &bp -> &bpr - new Transition (737, 738), // &brvba -> ¦ - new Transition (741, 742), // &Bsc -> &Bscr - new Transition (745, 746), // &bsc -> &bscr - new Transition (789, 1261), // &C -> &Cr - new Transition (796, 1256), // &c -> &cr - new Transition (797, 848), // &ca -> &car - new Transition (811, 812), // &capb -> &capbr - new Transition (836, 837), // &CapitalDiffe -> &CapitalDiffer - new Transition (862, 872), // &cca -> &ccar - new Transition (867, 868), // &Cca -> &Ccar - new Transition (886, 887), // &Cci -> &Ccir - new Transition (890, 891), // &cci -> &ccir - new Transition (938, 939), // &Cente -> &Center - new Transition (944, 945), // ¢e -> ¢er - new Transition (950, 951), // &Cf -> &Cfr - new Transition (953, 954), // &cf -> &cfr - new Transition (969, 970), // &checkma -> &checkmar - new Transition (978, 979), // &ci -> &cir - new Transition (988, 989), // &circlea -> &circlear - new Transition (989, 990), // &circlear -> &circlearr - new Transition (992, 998), // &circlearrow -> &circlearrowr - new Transition (1010, 1011), // &circledci -> &circledcir - new Transition (1019, 1020), // &Ci -> &Cir - new Transition (1065, 1066), // &cirsci -> &cirscir - new Transition (1081, 1082), // &ClockwiseContou -> &ClockwiseContour - new Transition (1087, 1088), // &ClockwiseContourInteg -> &ClockwiseContourIntegr - new Transition (1095, 1096), // &CloseCu -> &CloseCur - new Transition (1172, 1173), // &Cong -> &Congr - new Transition (1189, 1190), // &Contou -> &Contour - new Transition (1195, 1196), // &ContourInteg -> &ContourIntegr - new Transition (1200, 1210), // &Cop -> &Copr - new Transition (1203, 1206), // &cop -> &copr - new Transition (1223, 1224), // ©s -> ©sr - new Transition (1229, 1230), // &Counte -> &Counter - new Transition (1245, 1246), // &CounterClockwiseContou -> &CounterClockwiseContour - new Transition (1251, 1252), // &CounterClockwiseContourInteg -> &CounterClockwiseContourIntegr - new Transition (1257, 1258), // &cra -> &crar - new Transition (1258, 1259), // &crar -> &crarr - new Transition (1271, 1272), // &Csc -> &Cscr - new Transition (1275, 1276), // &csc -> &cscr - new Transition (1292, 1346), // &cu -> &cur - new Transition (1294, 1295), // &cuda -> &cudar - new Transition (1295, 1296), // &cudar -> &cudarr - new Transition (1296, 1299), // &cudarr -> &cudarrr - new Transition (1302, 1303), // &cuep -> &cuepr - new Transition (1309, 1310), // &cula -> &cular - new Transition (1310, 1311), // &cular -> &cularr - new Transition (1320, 1321), // &cupb -> &cupbr - new Transition (1341, 1342), // &cupo -> &cupor - new Transition (1346, 1377), // &cur -> &curr - new Transition (1347, 1348), // &cura -> &curar - new Transition (1348, 1349), // &curar -> &curarr - new Transition (1357, 1358), // &curlyeqp -> &curlyeqpr - new Transition (1383, 1384), // &curvea -> &curvear - new Transition (1384, 1385), // &curvear -> &curvearr - new Transition (1387, 1393), // &curvearrow -> &curvearrowr - new Transition (1426, 1444), // &Da -> &Dar - new Transition (1429, 1430), // &Dagge -> &Dagger - new Transition (1432, 2023), // &d -> &dr - new Transition (1433, 1451), // &da -> &dar - new Transition (1436, 1437), // &dagge -> &dagger - new Transition (1444, 1445), // &Dar -> &Darr - new Transition (1447, 1448), // &dA -> &dAr - new Transition (1448, 1449), // &dAr -> &dArr - new Transition (1451, 1452), // &dar -> &darr - new Transition (1465, 1466), // &dbka -> &dbkar - new Transition (1475, 1476), // &Dca -> &Dcar - new Transition (1481, 1482), // &dca -> &dcar - new Transition (1494, 1500), // &dda -> &ddar - new Transition (1497, 1498), // &ddagge -> &ddagger - new Transition (1500, 1501), // &ddar -> &ddarr - new Transition (1504, 1505), // &DDot -> &DDotr - new Transition (1535, 1544), // &df -> &dfr - new Transition (1541, 1542), // &Df -> &Dfr - new Transition (1547, 1548), // &dHa -> &dHar - new Transition (1551, 1552), // &dha -> &dhar - new Transition (1552, 1555), // &dhar -> &dharr - new Transition (1559, 1560), // &Diac -> &Diacr - new Transition (1587, 1588), // &DiacriticalG -> &DiacriticalGr - new Transition (1623, 1624), // &Diffe -> &Differ - new Transition (1670, 1675), // &dlc -> &dlcr - new Transition (1671, 1672), // &dlco -> &dlcor - new Transition (1682, 1683), // &dolla -> &dollar - new Transition (1727, 1728), // &dotsqua -> &dotsquar - new Transition (1736, 1737), // &doubleba -> &doublebar - new Transition (1753, 1754), // &DoubleContou -> &DoubleContour - new Transition (1759, 1760), // &DoubleContourInteg -> &DoubleContourIntegr - new Transition (1770, 1771), // &DoubleDownA -> &DoubleDownAr - new Transition (1771, 1772), // &DoubleDownAr -> &DoubleDownArr - new Transition (1780, 1781), // &DoubleLeftA -> &DoubleLeftAr - new Transition (1781, 1782), // &DoubleLeftAr -> &DoubleLeftArr - new Transition (1791, 1792), // &DoubleLeftRightA -> &DoubleLeftRightAr - new Transition (1792, 1793), // &DoubleLeftRightAr -> &DoubleLeftRightArr - new Transition (1808, 1809), // &DoubleLongLeftA -> &DoubleLongLeftAr - new Transition (1809, 1810), // &DoubleLongLeftAr -> &DoubleLongLeftArr - new Transition (1819, 1820), // &DoubleLongLeftRightA -> &DoubleLongLeftRightAr - new Transition (1820, 1821), // &DoubleLongLeftRightAr -> &DoubleLongLeftRightArr - new Transition (1830, 1831), // &DoubleLongRightA -> &DoubleLongRightAr - new Transition (1831, 1832), // &DoubleLongRightAr -> &DoubleLongRightArr - new Transition (1841, 1842), // &DoubleRightA -> &DoubleRightAr - new Transition (1842, 1843), // &DoubleRightAr -> &DoubleRightArr - new Transition (1853, 1854), // &DoubleUpA -> &DoubleUpAr - new Transition (1854, 1855), // &DoubleUpAr -> &DoubleUpArr - new Transition (1863, 1864), // &DoubleUpDownA -> &DoubleUpDownAr - new Transition (1864, 1865), // &DoubleUpDownAr -> &DoubleUpDownArr - new Transition (1870, 1871), // &DoubleVe -> &DoubleVer - new Transition (1878, 1879), // &DoubleVerticalBa -> &DoubleVerticalBar - new Transition (1883, 1884), // &DownA -> &DownAr - new Transition (1884, 1885), // &DownAr -> &DownArr - new Transition (1889, 1890), // &Downa -> &Downar - new Transition (1890, 1891), // &Downar -> &Downarr - new Transition (1897, 1898), // &downa -> &downar - new Transition (1898, 1899), // &downar -> &downarr - new Transition (1904, 1905), // &DownArrowBa -> &DownArrowBar - new Transition (1909, 1910), // &DownArrowUpA -> &DownArrowUpAr - new Transition (1910, 1911), // &DownArrowUpAr -> &DownArrowUpArr - new Transition (1915, 1916), // &DownB -> &DownBr - new Transition (1925, 1926), // &downdowna -> &downdownar - new Transition (1926, 1927), // &downdownar -> &downdownarr - new Transition (1933, 1934), // &downha -> &downhar - new Transition (1938, 1944), // &downharpoon -> &downharpoonr - new Transition (1963, 1964), // &DownLeftRightVecto -> &DownLeftRightVector - new Transition (1973, 1974), // &DownLeftTeeVecto -> &DownLeftTeeVector - new Transition (1980, 1981), // &DownLeftVecto -> &DownLeftVector - new Transition (1984, 1985), // &DownLeftVectorBa -> &DownLeftVectorBar - new Transition (1999, 2000), // &DownRightTeeVecto -> &DownRightTeeVector - new Transition (2006, 2007), // &DownRightVecto -> &DownRightVector - new Transition (2010, 2011), // &DownRightVectorBa -> &DownRightVectorBar - new Transition (2017, 2018), // &DownTeeA -> &DownTeeAr - new Transition (2018, 2019), // &DownTeeAr -> &DownTeeArr - new Transition (2026, 2027), // &drbka -> &drbkar - new Transition (2031, 2036), // &drc -> &drcr - new Transition (2032, 2033), // &drco -> &drcor - new Transition (2041, 2042), // &Dsc -> &Dscr - new Transition (2045, 2046), // &dsc -> &dscr - new Transition (2057, 2058), // &Dst -> &Dstr - new Transition (2062, 2063), // &dst -> &dstr - new Transition (2067, 2072), // &dt -> &dtr - new Transition (2078, 2079), // &dua -> &duar - new Transition (2079, 2080), // &duar -> &duarr - new Transition (2083, 2084), // &duha -> &duhar - new Transition (2102, 2103), // &dzig -> &dzigr - new Transition (2104, 2105), // &dzigra -> &dzigrar - new Transition (2105, 2106), // &dzigrar -> &dzigrarr - new Transition (2115, 2409), // &e -> &er - new Transition (2124, 2125), // &easte -> &easter - new Transition (2128, 2129), // &Eca -> &Ecar - new Transition (2134, 2135), // &eca -> &ecar - new Transition (2139, 2140), // &eci -> &ecir - new Transition (2142, 2143), // &Eci -> &Ecir - new Transition (2175, 2183), // &ef -> &efr - new Transition (2180, 2181), // &Ef -> &Efr - new Transition (2185, 2193), // &eg -> &egr - new Transition (2187, 2188), // &Eg -> &Egr - new Transition (2216, 2217), // &elinte -> &elinter - new Transition (2230, 2231), // &Emac -> &Emacr - new Transition (2235, 2236), // &emac -> &emacr - new Transition (2257, 2258), // &EmptySmallSqua -> &EmptySmallSquar - new Transition (2264, 2265), // &EmptyVe -> &EmptyVer - new Transition (2275, 2276), // &EmptyVerySmallSqua -> &EmptyVerySmallSquar - new Transition (2313, 2314), // &epa -> &epar - new Transition (2341, 2342), // &eqci -> &eqcir - new Transition (2359, 2360), // &eqslantgt -> &eqslantgtr - new Transition (2390, 2391), // &Equilib -> &Equilibr - new Transition (2404, 2405), // &eqvpa -> &eqvpar - new Transition (2410, 2411), // &era -> &erar - new Transition (2411, 2412), // &erar -> &erarr - new Transition (2419, 2420), // &Esc -> &Escr - new Transition (2423, 2424), // &esc -> &escr - new Transition (2451, 2455), // &eu -> &eur - new Transition (2503, 2647), // &f -> &fr - new Transition (2530, 2547), // &ff -> &ffr - new Transition (2544, 2545), // &Ff -> &Ffr - new Transition (2567, 2568), // &FilledSmallSqua -> &FilledSmallSquar - new Transition (2572, 2573), // &FilledVe -> &FilledVer - new Transition (2583, 2584), // &FilledVerySmallSqua -> &FilledVerySmallSquar - new Transition (2608, 2616), // &Fo -> &For - new Transition (2612, 2621), // &fo -> &for - new Transition (2630, 2631), // &Fou -> &Four - new Transition (2633, 2634), // &Fourie -> &Fourier - new Transition (2635, 2636), // &Fouriert -> &Fouriertr - new Transition (2640, 2641), // &fpa -> &fpar - new Transition (2694, 2695), // &Fsc -> &Fscr - new Transition (2698, 2699), // &fsc -> &fscr - new Transition (2701, 2861), // &g -> &gr - new Transition (2708, 2866), // &G -> &Gr - new Transition (2724, 2725), // &Gb -> &Gbr - new Transition (2730, 2731), // &gb -> &gbr - new Transition (2742, 2743), // &Gci -> &Gcir - new Transition (2747, 2748), // &gci -> &gcir - new Transition (2799, 2800), // &Gf -> &Gfr - new Transition (2802, 2803), // &gf -> &gfr - new Transition (2836, 2837), // &gnapp -> &gnappr - new Transition (2870, 2871), // &Greate -> &Greater - new Transition (2893, 2894), // &GreaterG -> &GreaterGr - new Transition (2898, 2899), // &GreaterGreate -> &GreaterGreater - new Transition (2924, 2925), // &Gsc -> &Gscr - new Transition (2928, 2929), // &gsc -> &gscr - new Transition (2942, 2965), // > -> >r - new Transition (2947, 2948), // >ci -> >cir - new Transition (2956, 2957), // >lPa -> >lPar - new Transition (2966, 2973), // >ra -> >rar - new Transition (2968, 2969), // >rapp -> >rappr - new Transition (2973, 2974), // >rar -> >rarr - new Transition (3003, 3004), // &gve -> &gver - new Transition (3021, 3041), // &ha -> &har - new Transition (3022, 3023), // &hai -> &hair - new Transition (3041, 3050), // &har -> &harr - new Transition (3046, 3047), // &hA -> &hAr - new Transition (3047, 3048), // &hAr -> &hArr - new Transition (3053, 3054), // &harrci -> &harrcir - new Transition (3061, 3062), // &hba -> &hbar - new Transition (3065, 3066), // &Hci -> &Hcir - new Transition (3070, 3071), // &hci -> &hcir - new Transition (3074, 3089), // &he -> &her - new Transition (3075, 3076), // &hea -> &hear - new Transition (3094, 3095), // &Hf -> &Hfr - new Transition (3097, 3098), // &hf -> &hfr - new Transition (3103, 3104), // &Hilbe -> &Hilber - new Transition (3115, 3116), // &hksea -> &hksear - new Transition (3121, 3122), // &hkswa -> &hkswar - new Transition (3126, 3166), // &ho -> &hor - new Transition (3127, 3128), // &hoa -> &hoar - new Transition (3128, 3129), // &hoar -> &hoarr - new Transition (3137, 3148), // &hook -> &hookr - new Transition (3142, 3143), // &hooklefta -> &hookleftar - new Transition (3143, 3144), // &hookleftar -> &hookleftarr - new Transition (3153, 3154), // &hookrighta -> &hookrightar - new Transition (3154, 3155), // &hookrightar -> &hookrightarr - new Transition (3159, 3171), // &Ho -> &Hor - new Transition (3168, 3169), // &horba -> &horbar - new Transition (3185, 3186), // &Hsc -> &Hscr - new Transition (3189, 3190), // &hsc -> &hscr - new Transition (3197, 3198), // &Hst -> &Hstr - new Transition (3202, 3203), // &hst -> &hstr - new Transition (3253, 3254), // &Ici -> &Icir - new Transition (3257, 3258), // &ici -> &icir - new Transition (3281, 3287), // &if -> &ifr - new Transition (3284, 3285), // &If -> &Ifr - new Transition (3289, 3290), // &Ig -> &Igr - new Transition (3295, 3296), // &ig -> &igr - new Transition (3333, 3334), // &Imac -> &Imacr - new Transition (3338, 3339), // &imac -> &imacr - new Transition (3347, 3348), // &Imagina -> &Imaginar - new Transition (3358, 3359), // &imagpa -> &imagpar - new Transition (3381, 3382), // &inca -> &incar - new Transition (3407, 3419), // &inte -> &inter - new Transition (3409, 3410), // &intege -> &integer - new Transition (3413, 3424), // &Inte -> &Inter - new Transition (3414, 3415), // &Integ -> &Integr - new Transition (3434, 3435), // &intla -> &intlar - new Transition (3439, 3440), // &intp -> &intpr - new Transition (3492, 3493), // &ip -> &ipr - new Transition (3504, 3505), // &Isc -> &Iscr - new Transition (3508, 3509), // &isc -> &iscr - new Transition (3557, 3558), // &Jci -> &Jcir - new Transition (3563, 3564), // &jci -> &jcir - new Transition (3571, 3572), // &Jf -> &Jfr - new Transition (3574, 3575), // &jf -> &jfr - new Transition (3591, 3592), // &Jsc -> &Jscr - new Transition (3595, 3596), // &jsc -> &jscr - new Transition (3598, 3599), // &Jse -> &Jser - new Transition (3603, 3604), // &jse -> &jser - new Transition (3648, 3649), // &Kf -> &Kfr - new Transition (3651, 3652), // &kf -> &kfr - new Transition (3654, 3655), // &kg -> &kgr - new Transition (3685, 3686), // &Ksc -> &Kscr - new Transition (3689, 3690), // &ksc -> &kscr - new Transition (3692, 4628), // &l -> &lr - new Transition (3693, 3762), // &lA -> &lAr - new Transition (3694, 3695), // &lAa -> &lAar - new Transition (3695, 3696), // &lAar -> &lAarr - new Transition (3699, 3759), // &La -> &Lar - new Transition (3705, 3765), // &la -> &lar - new Transition (3718, 3719), // &lag -> &lagr - new Transition (3751, 3752), // &Laplacet -> &Laplacetr - new Transition (3759, 3760), // &Lar -> &Larr - new Transition (3762, 3763), // &lAr -> &lArr - new Transition (3765, 3766), // &lar -> &larr - new Transition (3808, 3809), // &lBa -> &lBar - new Transition (3809, 3810), // &lBar -> &lBarr - new Transition (3812, 3821), // &lb -> &lbr - new Transition (3813, 3814), // &lba -> &lbar - new Transition (3814, 3815), // &lbar -> &lbarr - new Transition (3817, 3818), // &lbb -> &lbbr - new Transition (3838, 3839), // &Lca -> &Lcar - new Transition (3844, 3845), // &lca -> &lcar - new Transition (3869, 3879), // &ld -> &ldr - new Transition (3875, 3877), // &ldquo -> &ldquor - new Transition (3882, 3883), // &ldrdha -> &ldrdhar - new Transition (3888, 3889), // &ldrusha -> &ldrushar - new Transition (3900, 4041), // &Left -> &Leftr - new Transition (3901, 3914), // &LeftA -> &LeftAr - new Transition (3906, 3907), // &LeftAngleB -> &LeftAngleBr - new Transition (3914, 3915), // &LeftAr -> &LeftArr - new Transition (3919, 3920), // &Lefta -> &Leftar - new Transition (3920, 3921), // &Leftar -> &Leftarr - new Transition (3926, 4052), // &left -> &leftr - new Transition (3927, 3928), // &lefta -> &leftar - new Transition (3928, 3929), // &leftar -> &leftarr - new Transition (3934, 3935), // &LeftArrowBa -> &LeftArrowBar - new Transition (3942, 3943), // &LeftArrowRightA -> &LeftArrowRightAr - new Transition (3943, 3944), // &LeftArrowRightAr -> &LeftArrowRightArr - new Transition (3967, 3968), // &LeftDoubleB -> &LeftDoubleBr - new Transition (3984, 3985), // &LeftDownTeeVecto -> &LeftDownTeeVector - new Transition (3991, 3992), // &LeftDownVecto -> &LeftDownVector - new Transition (3995, 3996), // &LeftDownVectorBa -> &LeftDownVectorBar - new Transition (4001, 4002), // &LeftFloo -> &LeftFloor - new Transition (4005, 4006), // &leftha -> &lefthar - new Transition (4023, 4024), // &leftlefta -> &leftleftar - new Transition (4024, 4025), // &leftleftar -> &leftleftarr - new Transition (4035, 4036), // &LeftRightA -> &LeftRightAr - new Transition (4036, 4037), // &LeftRightAr -> &LeftRightArr - new Transition (4046, 4047), // &Leftrighta -> &Leftrightar - new Transition (4047, 4048), // &Leftrightar -> &Leftrightarr - new Transition (4057, 4058), // &leftrighta -> &leftrightar - new Transition (4058, 4059), // &leftrightar -> &leftrightarr - new Transition (4066, 4067), // &leftrightha -> &leftrighthar - new Transition (4079, 4080), // &leftrightsquiga -> &leftrightsquigar - new Transition (4080, 4081), // &leftrightsquigar -> &leftrightsquigarr - new Transition (4089, 4090), // &LeftRightVecto -> &LeftRightVector - new Transition (4092, 4120), // &LeftT -> &LeftTr - new Transition (4096, 4097), // &LeftTeeA -> &LeftTeeAr - new Transition (4097, 4098), // &LeftTeeAr -> &LeftTeeArr - new Transition (4106, 4107), // &LeftTeeVecto -> &LeftTeeVector - new Transition (4110, 4111), // &leftth -> &leftthr - new Transition (4129, 4130), // &LeftTriangleBa -> &LeftTriangleBar - new Transition (4148, 4149), // &LeftUpDownVecto -> &LeftUpDownVector - new Transition (4158, 4159), // &LeftUpTeeVecto -> &LeftUpTeeVector - new Transition (4165, 4166), // &LeftUpVecto -> &LeftUpVector - new Transition (4169, 4170), // &LeftUpVectorBa -> &LeftUpVectorBar - new Transition (4176, 4177), // &LeftVecto -> &LeftVector - new Transition (4180, 4181), // &LeftVectorBa -> &LeftVectorBar - new Transition (4206, 4208), // &lesdoto -> &lesdotor - new Transition (4218, 4219), // &lessapp -> &lessappr - new Transition (4230, 4231), // &lesseqgt -> &lesseqgtr - new Transition (4235, 4236), // &lesseqqgt -> &lesseqqgtr - new Transition (4245, 4246), // &LessEqualG -> &LessEqualGr - new Transition (4250, 4251), // &LessEqualGreate -> &LessEqualGreater - new Transition (4263, 4264), // &LessG -> &LessGr - new Transition (4268, 4269), // &LessGreate -> &LessGreater - new Transition (4272, 4273), // &lessgt -> &lessgtr - new Transition (4301, 4315), // &lf -> &lfr - new Transition (4309, 4310), // &lfloo -> &lfloor - new Transition (4312, 4313), // &Lf -> &Lfr - new Transition (4322, 4323), // &lHa -> &lHar - new Transition (4326, 4327), // &lha -> &lhar - new Transition (4350, 4351), // &lla -> &llar - new Transition (4351, 4352), // &llar -> &llarr - new Transition (4355, 4356), // &llco -> &llcor - new Transition (4358, 4359), // &llcorne -> &llcorner - new Transition (4364, 4365), // &Llefta -> &Lleftar - new Transition (4365, 4366), // &Lleftar -> &Lleftarr - new Transition (4371, 4372), // &llha -> &llhar - new Transition (4375, 4376), // &llt -> &lltr - new Transition (4405, 4406), // &lnapp -> &lnappr - new Transition (4423, 4427), // &loa -> &loar - new Transition (4427, 4428), // &loar -> &loarr - new Transition (4430, 4431), // &lob -> &lobr - new Transition (4436, 4520), // &Long -> &Longr - new Transition (4441, 4442), // &LongLeftA -> &LongLeftAr - new Transition (4442, 4443), // &LongLeftAr -> &LongLeftArr - new Transition (4450, 4480), // &Longleft -> &Longleftr - new Transition (4451, 4452), // &Longlefta -> &Longleftar - new Transition (4452, 4453), // &Longleftar -> &Longleftarr - new Transition (4458, 4531), // &long -> &longr - new Transition (4462, 4491), // &longleft -> &longleftr - new Transition (4463, 4464), // &longlefta -> &longleftar - new Transition (4464, 4465), // &longleftar -> &longleftarr - new Transition (4474, 4475), // &LongLeftRightA -> &LongLeftRightAr - new Transition (4475, 4476), // &LongLeftRightAr -> &LongLeftRightArr - new Transition (4485, 4486), // &Longleftrighta -> &Longleftrightar - new Transition (4486, 4487), // &Longleftrightar -> &Longleftrightarr - new Transition (4496, 4497), // &longleftrighta -> &longleftrightar - new Transition (4497, 4498), // &longleftrightar -> &longleftrightarr - new Transition (4514, 4515), // &LongRightA -> &LongRightAr - new Transition (4515, 4516), // &LongRightAr -> &LongRightArr - new Transition (4525, 4526), // &Longrighta -> &Longrightar - new Transition (4526, 4527), // &Longrightar -> &Longrightarr - new Transition (4536, 4537), // &longrighta -> &longrightar - new Transition (4537, 4538), // &longrightar -> &longrightarr - new Transition (4544, 4545), // &loopa -> &loopar - new Transition (4545, 4546), // &loopar -> &looparr - new Transition (4548, 4554), // &looparrow -> &looparrowr - new Transition (4561, 4562), // &lopa -> &lopar - new Transition (4585, 4586), // &lowba -> &lowbar - new Transition (4589, 4590), // &Lowe -> &Lower - new Transition (4595, 4596), // &LowerLeftA -> &LowerLeftAr - new Transition (4596, 4597), // &LowerLeftAr -> &LowerLeftArr - new Transition (4606, 4607), // &LowerRightA -> &LowerRightAr - new Transition (4607, 4608), // &LowerRightAr -> &LowerRightArr - new Transition (4622, 4623), // &lpa -> &lpar - new Transition (4629, 4630), // &lra -> &lrar - new Transition (4630, 4631), // &lrar -> &lrarr - new Transition (4634, 4635), // &lrco -> &lrcor - new Transition (4637, 4638), // &lrcorne -> &lrcorner - new Transition (4641, 4642), // &lrha -> &lrhar - new Transition (4648, 4649), // &lrt -> &lrtr - new Transition (4659, 4660), // &Lsc -> &Lscr - new Transition (4662, 4663), // &lsc -> &lscr - new Transition (4680, 4682), // &lsquo -> &lsquor - new Transition (4684, 4685), // &Lst -> &Lstr - new Transition (4689, 4690), // &lst -> &lstr - new Transition (4698, 4731), // < -> <r - new Transition (4703, 4704), // <ci -> <cir - new Transition (4710, 4711), // <h -> <hr - new Transition (4721, 4722), // <la -> <lar - new Transition (4722, 4723), // <lar -> <larr - new Transition (4739, 4740), // <rPa -> <rPar - new Transition (4742, 4743), // &lu -> &lur - new Transition (4747, 4748), // &lurdsha -> &lurdshar - new Transition (4752, 4753), // &luruha -> &luruhar - new Transition (4756, 4757), // &lve -> &lver - new Transition (4768, 4804), // &ma -> &mar - new Transition (4769, 4770), // &mac -> ¯ - new Transition (4806, 4807), // &marke -> &marker - new Transition (4833, 4834), // &measu -> &measur - new Transition (4858, 4859), // &Mellint -> &Mellintr - new Transition (4862, 4863), // &Mf -> &Mfr - new Transition (4865, 4866), // &mf -> &mfr - new Transition (4872, 4873), // &mic -> &micr - new Transition (4883, 4884), // &midci -> &midcir - new Transition (4913, 4914), // &mld -> &mldr - new Transition (4938, 4939), // &Msc -> &Mscr - new Transition (4942, 4943), // &msc -> &mscr - new Transition (4965, 5855), // &n -> &nr - new Transition (4996, 4997), // &napp -> &nappr - new Transition (5002, 5003), // &natu -> &natur - new Transition (5021, 5030), // &nca -> &ncar - new Transition (5025, 5026), // &Nca -> &Ncar - new Transition (5066, 5067), // &nea -> &near - new Transition (5067, 5075), // &near -> &nearr - new Transition (5071, 5072), // &neA -> &neAr - new Transition (5072, 5073), // &neAr -> &neArr - new Transition (5122, 5123), // &NegativeVe -> &NegativeVer - new Transition (5142, 5143), // &nesea -> &nesear - new Transition (5152, 5153), // &NestedG -> &NestedGr - new Transition (5157, 5158), // &NestedGreate -> &NestedGreater - new Transition (5159, 5160), // &NestedGreaterG -> &NestedGreaterGr - new Transition (5164, 5165), // &NestedGreaterGreate -> &NestedGreaterGreater - new Transition (5189, 5190), // &Nf -> &Nfr - new Transition (5192, 5193), // &nf -> &nfr - new Transition (5221, 5223), // &ngt -> &ngtr - new Transition (5228, 5229), // &nhA -> &nhAr - new Transition (5229, 5230), // &nhAr -> &nhArr - new Transition (5232, 5233), // &nha -> &nhar - new Transition (5233, 5234), // &nhar -> &nharr - new Transition (5237, 5238), // &nhpa -> &nhpar - new Transition (5257, 5258), // &nlA -> &nlAr - new Transition (5258, 5259), // &nlAr -> &nlArr - new Transition (5261, 5262), // &nla -> &nlar - new Transition (5262, 5263), // &nlar -> &nlarr - new Transition (5265, 5266), // &nld -> &nldr - new Transition (5275, 5290), // &nLeft -> &nLeftr - new Transition (5276, 5277), // &nLefta -> &nLeftar - new Transition (5277, 5278), // &nLeftar -> &nLeftarr - new Transition (5283, 5301), // &nleft -> &nleftr - new Transition (5284, 5285), // &nlefta -> &nleftar - new Transition (5285, 5286), // &nleftar -> &nleftarr - new Transition (5295, 5296), // &nLeftrighta -> &nLeftrightar - new Transition (5296, 5297), // &nLeftrightar -> &nLeftrightarr - new Transition (5306, 5307), // &nleftrighta -> &nleftrightar - new Transition (5307, 5308), // &nleftrightar -> &nleftrightarr - new Transition (5334, 5336), // &nlt -> &nltr - new Transition (5348, 5349), // &NoB -> &NoBr - new Transition (5355, 5356), // &NonB -> &NonBr - new Transition (5383, 5384), // &NotCong -> &NotCongr - new Transition (5403, 5404), // &NotDoubleVe -> &NotDoubleVer - new Transition (5411, 5412), // &NotDoubleVerticalBa -> &NotDoubleVerticalBar - new Transition (5439, 5440), // &NotG -> &NotGr - new Transition (5444, 5445), // &NotGreate -> &NotGreater - new Transition (5463, 5464), // &NotGreaterG -> &NotGreaterGr - new Transition (5468, 5469), // &NotGreaterGreate -> &NotGreaterGreater - new Transition (5532, 5533), // &NotLeftT -> &NotLeftTr - new Transition (5542, 5543), // &NotLeftTriangleBa -> &NotLeftTriangleBar - new Transition (5560, 5561), // &NotLessG -> &NotLessGr - new Transition (5565, 5566), // &NotLessGreate -> &NotLessGreater - new Transition (5596, 5597), // &NotNestedG -> &NotNestedGr - new Transition (5601, 5602), // &NotNestedGreate -> &NotNestedGreater - new Transition (5603, 5604), // &NotNestedGreaterG -> &NotNestedGreaterGr - new Transition (5608, 5609), // &NotNestedGreaterGreate -> &NotNestedGreaterGreater - new Transition (5630, 5631), // &NotP -> &NotPr - new Transition (5659, 5660), // &NotReve -> &NotRever - new Transition (5675, 5676), // &NotRightT -> &NotRightTr - new Transition (5685, 5686), // &NotRightTriangleBa -> &NotRightTriangleBar - new Transition (5697, 5698), // &NotSqua -> &NotSquar - new Transition (5714, 5715), // &NotSquareSupe -> &NotSquareSuper - new Transition (5769, 5770), // &NotSupe -> &NotSuper - new Transition (5810, 5811), // &NotVe -> &NotVer - new Transition (5818, 5819), // &NotVerticalBa -> &NotVerticalBar - new Transition (5821, 5842), // &np -> &npr - new Transition (5822, 5823), // &npa -> &npar - new Transition (5856, 5857), // &nrA -> &nrAr - new Transition (5857, 5858), // &nrAr -> &nrArr - new Transition (5860, 5861), // &nra -> &nrar - new Transition (5861, 5862), // &nrar -> &nrarr - new Transition (5873, 5874), // &nRighta -> &nRightar - new Transition (5874, 5875), // &nRightar -> &nRightarr - new Transition (5883, 5884), // &nrighta -> &nrightar - new Transition (5884, 5885), // &nrightar -> &nrightarr - new Transition (5889, 5890), // &nrt -> &nrtr - new Transition (5896, 5908), // &nsc -> &nscr - new Transition (5905, 5906), // &Nsc -> &Nscr - new Transition (5911, 5912), // &nsho -> &nshor - new Transition (5919, 5920), // &nshortpa -> &nshortpar - new Transition (5939, 5940), // &nspa -> &nspar - new Transition (5988, 6006), // &nt -> &ntr - new Transition (6012, 6021), // &ntriangle -> &ntriangler - new Transition (6036, 6037), // &nume -> &numer - new Transition (6043, 6097), // &nv -> &nvr - new Transition (6074, 6075), // &nvHa -> &nvHar - new Transition (6075, 6076), // &nvHar -> &nvHarr - new Transition (6085, 6086), // &nvlA -> &nvlAr - new Transition (6086, 6087), // &nvlAr -> &nvlArr - new Transition (6091, 6093), // &nvlt -> &nvltr - new Transition (6098, 6099), // &nvrA -> &nvrAr - new Transition (6099, 6100), // &nvrAr -> &nvrArr - new Transition (6102, 6103), // &nvrt -> &nvrtr - new Transition (6112, 6113), // &nwa -> &nwar - new Transition (6113, 6121), // &nwar -> &nwarr - new Transition (6117, 6118), // &nwA -> &nwAr - new Transition (6118, 6119), // &nwAr -> &nwArr - new Transition (6128, 6129), // &nwnea -> &nwnear - new Transition (6131, 6340), // &O -> &Or - new Transition (6138, 6342), // &o -> &or - new Transition (6149, 6150), // &oci -> &ocir - new Transition (6153, 6154), // &Oci -> &Ocir - new Transition (6200, 6208), // &of -> &ofr - new Transition (6202, 6203), // &ofci -> &ofcir - new Transition (6205, 6206), // &Of -> &Ofr - new Transition (6210, 6220), // &og -> &ogr - new Transition (6214, 6215), // &Og -> &Ogr - new Transition (6229, 6230), // &ohba -> &ohbar - new Transition (6239, 6240), // &ola -> &olar - new Transition (6240, 6241), // &olar -> &olarr - new Transition (6243, 6247), // &olc -> &olcr - new Transition (6244, 6245), // &olci -> &olcir - new Transition (6260, 6261), // &Omac -> &Omacr - new Transition (6265, 6266), // &omac -> &omacr - new Transition (6277, 6278), // &Omic -> &Omicr - new Transition (6283, 6284), // &omic -> &omicr - new Transition (6303, 6304), // &opa -> &opar - new Transition (6310, 6311), // &OpenCu -> &OpenCur - new Transition (6332, 6333), // &ope -> &oper - new Transition (6344, 6345), // &ora -> &orar - new Transition (6345, 6346), // &orar -> &orarr - new Transition (6350, 6351), // &orde -> &order - new Transition (6365, 6366), // &oro -> &oror - new Transition (6379, 6380), // &Osc -> &Oscr - new Transition (6383, 6384), // &osc -> &oscr - new Transition (6432, 6433), // &ovba -> &ovbar - new Transition (6436, 6437), // &Ove -> &Over - new Transition (6438, 6442), // &OverB -> &OverBr - new Transition (6439, 6440), // &OverBa -> &OverBar - new Transition (6452, 6453), // &OverPa -> &OverPar - new Transition (6463, 6642), // &p -> &pr - new Transition (6464, 6465), // &pa -> &par - new Transition (6482, 6640), // &P -> &Pr - new Transition (6483, 6484), // &Pa -> &Par - new Transition (6497, 6498), // &pe -> &per - new Transition (6518, 6519), // &Pf -> &Pfr - new Transition (6521, 6522), // &pf -> &pfr - new Transition (6549, 6550), // &pitchfo -> &pitchfor - new Transition (6571, 6572), // &plusaci -> &plusacir - new Transition (6577, 6578), // &plusci -> &pluscir - new Transition (6613, 6614), // &Poinca -> &Poincar - new Transition (6659, 6660), // &precapp -> &precappr - new Transition (6665, 6666), // &preccu -> &preccur - new Transition (6708, 6709), // &precnapp -> &precnappr - new Transition (6757, 6758), // &profala -> &profalar - new Transition (6766, 6767), // &profsu -> &profsur - new Transition (6773, 6774), // &Propo -> &Propor - new Transition (6790, 6791), // &pru -> &prur - new Transition (6796, 6797), // &Psc -> &Pscr - new Transition (6800, 6801), // &psc -> &pscr - new Transition (6814, 6815), // &Qf -> &Qfr - new Transition (6818, 6819), // &qf -> &qfr - new Transition (6833, 6834), // &qp -> &qpr - new Transition (6840, 6841), // &Qsc -> &Qscr - new Transition (6844, 6845), // &qsc -> &qscr - new Transition (6850, 6851), // &quate -> &quater - new Transition (6876, 7526), // &r -> &rr - new Transition (6877, 6928), // &rA -> &rAr - new Transition (6878, 6879), // &rAa -> &rAar - new Transition (6879, 6880), // &rAar -> &rAarr - new Transition (6882, 6931), // &ra -> &rar - new Transition (6886, 7531), // &R -> &Rr - new Transition (6887, 6925), // &Ra -> &Rar - new Transition (6925, 6926), // &Rar -> &Rarr - new Transition (6928, 6929), // &rAr -> &rArr - new Transition (6931, 6932), // &rar -> &rarr - new Transition (6987, 6988), // &RBa -> &RBar - new Transition (6988, 6989), // &RBar -> &RBarr - new Transition (6992, 6993), // &rBa -> &rBar - new Transition (6993, 6994), // &rBar -> &rBarr - new Transition (6996, 7005), // &rb -> &rbr - new Transition (6997, 6998), // &rba -> &rbar - new Transition (6998, 6999), // &rbar -> &rbarr - new Transition (7001, 7002), // &rbb -> &rbbr - new Transition (7022, 7023), // &Rca -> &Rcar - new Transition (7028, 7029), // &rca -> &rcar - new Transition (7060, 7061), // &rdldha -> &rdldhar - new Transition (7065, 7067), // &rdquo -> &rdquor - new Transition (7083, 7084), // &realpa -> &realpar - new Transition (7098, 7099), // &Reve -> &Rever - new Transition (7115, 7116), // &ReverseEquilib -> &ReverseEquilibr - new Transition (7129, 7130), // &ReverseUpEquilib -> &ReverseUpEquilibr - new Transition (7135, 7149), // &rf -> &rfr - new Transition (7143, 7144), // &rfloo -> &rfloor - new Transition (7146, 7147), // &Rf -> &Rfr - new Transition (7152, 7153), // &rHa -> &rHar - new Transition (7156, 7157), // &rha -> &rhar - new Transition (7175, 7188), // &RightA -> &RightAr - new Transition (7180, 7181), // &RightAngleB -> &RightAngleBr - new Transition (7188, 7189), // &RightAr -> &RightArr - new Transition (7193, 7194), // &Righta -> &Rightar - new Transition (7194, 7195), // &Rightar -> &Rightarr - new Transition (7202, 7314), // &right -> &rightr - new Transition (7203, 7204), // &righta -> &rightar - new Transition (7204, 7205), // &rightar -> &rightarr - new Transition (7210, 7211), // &RightArrowBa -> &RightArrowBar - new Transition (7217, 7218), // &RightArrowLeftA -> &RightArrowLeftAr - new Transition (7218, 7219), // &RightArrowLeftAr -> &RightArrowLeftArr - new Transition (7242, 7243), // &RightDoubleB -> &RightDoubleBr - new Transition (7259, 7260), // &RightDownTeeVecto -> &RightDownTeeVector - new Transition (7266, 7267), // &RightDownVecto -> &RightDownVector - new Transition (7270, 7271), // &RightDownVectorBa -> &RightDownVectorBar - new Transition (7276, 7277), // &RightFloo -> &RightFloor - new Transition (7280, 7281), // &rightha -> &righthar - new Transition (7298, 7299), // &rightlefta -> &rightleftar - new Transition (7299, 7300), // &rightleftar -> &rightleftarr - new Transition (7306, 7307), // &rightleftha -> &rightlefthar - new Transition (7319, 7320), // &rightrighta -> &rightrightar - new Transition (7320, 7321), // &rightrightar -> &rightrightarr - new Transition (7331, 7332), // &rightsquiga -> &rightsquigar - new Transition (7332, 7333), // &rightsquigar -> &rightsquigarr - new Transition (7337, 7365), // &RightT -> &RightTr - new Transition (7341, 7342), // &RightTeeA -> &RightTeeAr - new Transition (7342, 7343), // &RightTeeAr -> &RightTeeArr - new Transition (7351, 7352), // &RightTeeVecto -> &RightTeeVector - new Transition (7355, 7356), // &rightth -> &rightthr - new Transition (7374, 7375), // &RightTriangleBa -> &RightTriangleBar - new Transition (7393, 7394), // &RightUpDownVecto -> &RightUpDownVector - new Transition (7403, 7404), // &RightUpTeeVecto -> &RightUpTeeVector - new Transition (7410, 7411), // &RightUpVecto -> &RightUpVector - new Transition (7414, 7415), // &RightUpVectorBa -> &RightUpVectorBar - new Transition (7421, 7422), // &RightVecto -> &RightVector - new Transition (7425, 7426), // &RightVectorBa -> &RightVectorBar - new Transition (7443, 7444), // &rla -> &rlar - new Transition (7444, 7445), // &rlar -> &rlarr - new Transition (7448, 7449), // &rlha -> &rlhar - new Transition (7470, 7474), // &roa -> &roar - new Transition (7474, 7475), // &roar -> &roarr - new Transition (7477, 7478), // &rob -> &robr - new Transition (7482, 7483), // &ropa -> &ropar - new Transition (7513, 7514), // &rpa -> &rpar - new Transition (7527, 7528), // &rra -> &rrar - new Transition (7528, 7529), // &rrar -> &rrarr - new Transition (7536, 7537), // &Rrighta -> &Rrightar - new Transition (7537, 7538), // &Rrightar -> &Rrightarr - new Transition (7549, 7550), // &Rsc -> &Rscr - new Transition (7552, 7553), // &rsc -> &rscr - new Transition (7563, 7565), // &rsquo -> &rsquor - new Transition (7567, 7578), // &rt -> &rtr - new Transition (7568, 7569), // &rth -> &rthr - new Transition (7586, 7587), // &rtrilt -> &rtriltr - new Transition (7605, 7606), // &ruluha -> &ruluhar - new Transition (7617, 8068), // &s -> &sr - new Transition (7633, 7641), // &sca -> &scar - new Transition (7636, 7637), // &Sca -> &Scar - new Transition (7662, 7663), // &Sci -> &Scir - new Transition (7666, 7667), // &sci -> &scir - new Transition (7704, 7705), // &sea -> &sear - new Transition (7705, 7713), // &sear -> &searr - new Transition (7709, 7710), // &seA -> &seAr - new Transition (7710, 7711), // &seAr -> &seArr - new Transition (7726, 7727), // &seswa -> &seswar - new Transition (7741, 7742), // &Sf -> &Sfr - new Transition (7744, 7745), // &sf -> &sfr - new Transition (7752, 7753), // &sha -> &shar - new Transition (7773, 7774), // &Sho -> &Shor - new Transition (7780, 7781), // &ShortDownA -> &ShortDownAr - new Transition (7781, 7782), // &ShortDownAr -> &ShortDownArr - new Transition (7790, 7791), // &ShortLeftA -> &ShortLeftAr - new Transition (7791, 7792), // &ShortLeftAr -> &ShortLeftArr - new Transition (7796, 7797), // &sho -> &shor - new Transition (7804, 7805), // &shortpa -> &shortpar - new Transition (7817, 7818), // &ShortRightA -> &ShortRightAr - new Transition (7818, 7819), // &ShortRightAr -> &ShortRightArr - new Transition (7825, 7826), // &ShortUpA -> &ShortUpAr - new Transition (7826, 7827), // &ShortUpAr -> &ShortUpArr - new Transition (7847, 7873), // &sim -> &simr - new Transition (7874, 7875), // &simra -> &simrar - new Transition (7875, 7876), // &simrar -> &simrarr - new Transition (7879, 7880), // &sla -> &slar - new Transition (7880, 7881), // &slar -> &slarr - new Transition (7888, 7889), // &SmallCi -> &SmallCir - new Transition (7913, 7914), // &smepa -> &smepar - new Transition (7946, 7947), // &solba -> &solbar - new Transition (7957, 7966), // &spa -> &spar - new Transition (7980, 7981), // &Sq -> &Sqr - new Transition (8011, 8012), // &Squa -> &Squar - new Transition (8015, 8016), // &squa -> &squar - new Transition (8022, 8023), // &SquareInte -> &SquareInter - new Transition (8046, 8047), // &SquareSupe -> &SquareSuper - new Transition (8069, 8070), // &sra -> &srar - new Transition (8070, 8071), // &srar -> &srarr - new Transition (8074, 8075), // &Ssc -> &Sscr - new Transition (8078, 8079), // &ssc -> &sscr - new Transition (8092, 8093), // &ssta -> &sstar - new Transition (8097, 8098), // &Sta -> &Star - new Transition (8100, 8106), // &st -> &str - new Transition (8101, 8102), // &sta -> &star - new Transition (8131, 8160), // &sub -> &subr - new Transition (8161, 8162), // &subra -> &subrar - new Transition (8162, 8163), // &subrar -> &subrarr - new Transition (8203, 8204), // &succapp -> &succappr - new Transition (8209, 8210), // &succcu -> &succcur - new Transition (8252, 8253), // &succnapp -> &succnappr - new Transition (8308, 8309), // &Supe -> &Super - new Transition (8329, 8330), // &supla -> &suplar - new Transition (8330, 8331), // &suplar -> &suplarr - new Transition (8376, 8377), // &swa -> &swar - new Transition (8377, 8385), // &swar -> &swarr - new Transition (8381, 8382), // &swA -> &swAr - new Transition (8382, 8383), // &swAr -> &swArr - new Transition (8392, 8393), // &swnwa -> &swnwar - new Transition (8400, 8676), // &T -> &Tr - new Transition (8404, 8628), // &t -> &tr - new Transition (8405, 8406), // &ta -> &tar - new Transition (8415, 8416), // &tb -> &tbr - new Transition (8420, 8421), // &Tca -> &Tcar - new Transition (8426, 8427), // &tca -> &tcar - new Transition (8450, 8451), // &tel -> &telr - new Transition (8455, 8456), // &Tf -> &Tfr - new Transition (8458, 8459), // &tf -> &tfr - new Transition (8462, 8463), // &the -> &ther - new Transition (8468, 8469), // &The -> &Ther - new Transition (8472, 8473), // &Therefo -> &Therefor - new Transition (8477, 8478), // &therefo -> &therefor - new Transition (8498, 8499), // &thickapp -> &thickappr - new Transition (8540, 8541), // &tho -> &thor - new Transition (8582, 8583), // ×ba -> ×bar - new Transition (8601, 8602), // &topci -> &topcir - new Transition (8610, 8611), // &topfo -> &topfor - new Transition (8617, 8618), // &tp -> &tpr - new Transition (8638, 8655), // &triangle -> &triangler - new Transition (8706, 8707), // &Tsc -> &Tscr - new Transition (8710, 8711), // &tsc -> &tscr - new Transition (8727, 8728), // &Tst -> &Tstr - new Transition (8732, 8733), // &tst -> &tstr - new Transition (8746, 8757), // &twohead -> &twoheadr - new Transition (8751, 8752), // &twoheadlefta -> &twoheadleftar - new Transition (8752, 8753), // &twoheadleftar -> &twoheadleftarr - new Transition (8762, 8763), // &twoheadrighta -> &twoheadrightar - new Transition (8763, 8764), // &twoheadrightar -> &twoheadrightarr - new Transition (8768, 9140), // &U -> &Ur - new Transition (8769, 8782), // &Ua -> &Uar - new Transition (8775, 9127), // &u -> &ur - new Transition (8776, 8789), // &ua -> &uar - new Transition (8782, 8783), // &Uar -> &Uarr - new Transition (8785, 8786), // &uA -> &uAr - new Transition (8786, 8787), // &uAr -> &uArr - new Transition (8789, 8790), // &uar -> &uarr - new Transition (8794, 8795), // &Uarroci -> &Uarrocir - new Transition (8797, 8798), // &Ub -> &Ubr - new Transition (8802, 8803), // &ub -> &ubr - new Transition (8816, 8817), // &Uci -> &Ucir - new Transition (8821, 8822), // &uci -> &ucir - new Transition (8830, 8831), // &uda -> &udar - new Transition (8831, 8832), // &udar -> &udarr - new Transition (8846, 8847), // &udha -> &udhar - new Transition (8849, 8858), // &uf -> &ufr - new Transition (8855, 8856), // &Uf -> &Ufr - new Transition (8860, 8861), // &Ug -> &Ugr - new Transition (8866, 8867), // &ug -> &ugr - new Transition (8873, 8874), // &uHa -> &uHar - new Transition (8877, 8878), // &uha -> &uhar - new Transition (8878, 8881), // &uhar -> &uharr - new Transition (8888, 8896), // &ulc -> &ulcr - new Transition (8889, 8890), // &ulco -> &ulcor - new Transition (8893, 8894), // &ulcorne -> &ulcorner - new Transition (8900, 8901), // &ult -> &ultr - new Transition (8906, 8907), // &Umac -> &Umacr - new Transition (8911, 8912), // &umac -> &umacr - new Transition (8918, 8919), // &Unde -> &Under - new Transition (8920, 8924), // &UnderB -> &UnderBr - new Transition (8921, 8922), // &UnderBa -> &UnderBar - new Transition (8934, 8935), // &UnderPa -> &UnderPar - new Transition (8971, 8972), // &UpA -> &UpAr - new Transition (8972, 8973), // &UpAr -> &UpArr - new Transition (8977, 8978), // &Upa -> &Upar - new Transition (8978, 8979), // &Upar -> &Uparr - new Transition (8984, 8985), // &upa -> &upar - new Transition (8985, 8986), // &upar -> &uparr - new Transition (8991, 8992), // &UpArrowBa -> &UpArrowBar - new Transition (8998, 8999), // &UpArrowDownA -> &UpArrowDownAr - new Transition (8999, 9000), // &UpArrowDownAr -> &UpArrowDownArr - new Transition (9008, 9009), // &UpDownA -> &UpDownAr - new Transition (9009, 9010), // &UpDownAr -> &UpDownArr - new Transition (9018, 9019), // &Updowna -> &Updownar - new Transition (9019, 9020), // &Updownar -> &Updownarr - new Transition (9028, 9029), // &updowna -> &updownar - new Transition (9029, 9030), // &updownar -> &updownarr - new Transition (9040, 9041), // &UpEquilib -> &UpEquilibr - new Transition (9047, 9048), // &upha -> &uphar - new Transition (9052, 9058), // &upharpoon -> &upharpoonr - new Transition (9069, 9070), // &Uppe -> &Upper - new Transition (9075, 9076), // &UpperLeftA -> &UpperLeftAr - new Transition (9076, 9077), // &UpperLeftAr -> &UpperLeftArr - new Transition (9086, 9087), // &UpperRightA -> &UpperRightAr - new Transition (9087, 9088), // &UpperRightAr -> &UpperRightArr - new Transition (9112, 9113), // &UpTeeA -> &UpTeeAr - new Transition (9113, 9114), // &UpTeeAr -> &UpTeeArr - new Transition (9120, 9121), // &upupa -> &upupar - new Transition (9121, 9122), // &upupar -> &upuparr - new Transition (9128, 9136), // &urc -> &urcr - new Transition (9129, 9130), // &urco -> &urcor - new Transition (9133, 9134), // &urcorne -> &urcorner - new Transition (9149, 9150), // &urt -> &urtr - new Transition (9154, 9155), // &Usc -> &Uscr - new Transition (9158, 9159), // &usc -> &uscr - new Transition (9161, 9177), // &ut -> &utr - new Transition (9183, 9184), // &uua -> &uuar - new Transition (9184, 9185), // &uuar -> &uuarr - new Transition (9201, 9445), // &v -> &vr - new Transition (9202, 9208), // &va -> &var - new Transition (9204, 9205), // &vang -> &vangr - new Transition (9208, 9247), // &var -> &varr - new Transition (9231, 9237), // &varp -> &varpr - new Transition (9243, 9244), // &vA -> &vAr - new Transition (9244, 9245), // &vAr -> &vArr - new Transition (9279, 9285), // &vart -> &vartr - new Transition (9291, 9297), // &vartriangle -> &vartriangler - new Transition (9305, 9306), // &Vba -> &Vbar - new Transition (9309, 9310), // &vBa -> &vBar - new Transition (9342, 9360), // &Ve -> &Ver - new Transition (9345, 9365), // &ve -> &ver - new Transition (9349, 9350), // &veeba -> &veebar - new Transition (9362, 9363), // &Verba -> &Verbar - new Transition (9367, 9368), // &verba -> &verbar - new Transition (9379, 9380), // &VerticalBa -> &VerticalBar - new Transition (9390, 9391), // &VerticalSepa -> &VerticalSepar - new Transition (9394, 9395), // &VerticalSeparato -> &VerticalSeparator - new Transition (9414, 9415), // &Vf -> &Vfr - new Transition (9417, 9418), // &vf -> &vfr - new Transition (9421, 9422), // &vlt -> &vltr - new Transition (9440, 9441), // &vp -> &vpr - new Transition (9446, 9447), // &vrt -> &vrtr - new Transition (9451, 9452), // &Vsc -> &Vscr - new Transition (9455, 9456), // &vsc -> &vscr - new Transition (9486, 9487), // &Wci -> &Wcir - new Transition (9490, 9533), // &w -> &wr - new Transition (9492, 9493), // &wci -> &wcir - new Transition (9499, 9500), // &wedba -> &wedbar - new Transition (9513, 9514), // &weie -> &weier - new Transition (9517, 9518), // &Wf -> &Wfr - new Transition (9520, 9521), // &wf -> &wfr - new Transition (9541, 9542), // &Wsc -> &Wscr - new Transition (9545, 9546), // &wsc -> &wscr - new Transition (9548, 9623), // &x -> &xr - new Transition (9553, 9554), // &xci -> &xcir - new Transition (9561, 9562), // &xdt -> &xdtr - new Transition (9566, 9567), // &Xf -> &Xfr - new Transition (9569, 9570), // &xf -> &xfr - new Transition (9573, 9574), // &xhA -> &xhAr - new Transition (9574, 9575), // &xhAr -> &xhArr - new Transition (9577, 9578), // &xha -> &xhar - new Transition (9578, 9579), // &xhar -> &xharr - new Transition (9586, 9587), // &xlA -> &xlAr - new Transition (9587, 9588), // &xlAr -> &xlArr - new Transition (9590, 9591), // &xla -> &xlar - new Transition (9591, 9592), // &xlar -> &xlarr - new Transition (9624, 9625), // &xrA -> &xrAr - new Transition (9625, 9626), // &xrAr -> &xrArr - new Transition (9628, 9629), // &xra -> &xrar - new Transition (9629, 9630), // &xrar -> &xrarr - new Transition (9633, 9634), // &Xsc -> &Xscr - new Transition (9637, 9638), // &xsc -> &xscr - new Transition (9651, 9652), // &xut -> &xutr - new Transition (9686, 9687), // &Yci -> &Ycir - new Transition (9691, 9692), // &yci -> &ycir - new Transition (9702, 9703), // &Yf -> &Yfr - new Transition (9705, 9706), // &yf -> &yfr - new Transition (9725, 9726), // &Ysc -> &Yscr - new Transition (9729, 9730), // &ysc -> &yscr - new Transition (9762, 9763), // &Zca -> &Zcar - new Transition (9768, 9769), // &zca -> &zcar - new Transition (9787, 9788), // &zeet -> &zeetr - new Transition (9791, 9792), // &Ze -> &Zer - new Transition (9811, 9812), // &Zf -> &Zfr - new Transition (9814, 9815), // &zf -> &zfr - new Transition (9826, 9827), // &zig -> &zigr - new Transition (9828, 9829), // &zigra -> &zigrar - new Transition (9829, 9830), // &zigrar -> &zigrarr - new Transition (9841, 9842), // &Zsc -> &Zscr - new Transition (9845, 9846) // &zsc -> &zscr - }; - TransitionTable_s = new Transition[368] { - new Transition (0, 7617), // & -> &s - new Transition (1, 247), // &A -> &As - new Transition (8, 251), // &a -> &as - new Transition (81, 82), // &alef -> &alefs - new Transition (120, 128), // &and -> &ands - new Transition (136, 172), // &ang -> &angs - new Transition (143, 144), // &angm -> &angms - new Transition (213, 214), // &apo -> &apos - new Transition (247, 255), // &As -> &Ass - new Transition (301, 744), // &b -> &bs - new Transition (304, 324), // &back -> &backs - new Transition (311, 312), // &backep -> &backeps - new Transition (331, 740), // &B -> &Bs - new Transition (334, 335), // &Back -> &Backs - new Transition (337, 338), // &Backsla -> &Backslas - new Transition (387, 388), // &becau -> &becaus - new Transition (393, 394), // &Becau -> &Becaus - new Transition (405, 406), // &bep -> &beps - new Transition (420, 421), // &Bernoulli -> &Bernoullis - new Transition (443, 471), // &big -> &bigs - new Transition (462, 463), // &bigoplu -> &bigoplus - new Transition (468, 469), // &bigotime -> &bigotimes - new Transition (500, 501), // &biguplu -> &biguplus - new Transition (522, 531), // &black -> &blacks - new Transition (659, 660), // &boxminu -> &boxminus - new Transition (664, 665), // &boxplu -> &boxplus - new Transition (670, 671), // &boxtime -> &boxtimes - new Transition (762, 763), // &bsolh -> &bsolhs - new Transition (789, 1270), // &C -> &Cs - new Transition (796, 1274), // &c -> &cs - new Transition (805, 846), // &cap -> &caps - new Transition (858, 859), // &Cayley -> &Cayleys - new Transition (863, 864), // &ccap -> &ccaps - new Transition (901, 902), // &ccup -> &ccups - new Transition (902, 904), // &ccups -> &ccupss - new Transition (979, 1063), // &cir -> &cirs - new Transition (1005, 1006), // &circleda -> &circledas - new Transition (1015, 1016), // &circledda -> &circleddas - new Transition (1035, 1036), // &CircleMinu -> &CircleMinus - new Transition (1040, 1041), // &CirclePlu -> &CirclePlus - new Transition (1046, 1047), // &CircleTime -> &CircleTimes - new Transition (1069, 1092), // &Clo -> &Clos - new Transition (1073, 1074), // &Clockwi -> &Clockwis - new Transition (1119, 1120), // &club -> &clubs - new Transition (1161, 1162), // &complexe -> &complexes - new Transition (1221, 1223), // © -> ©s - new Transition (1237, 1238), // &CounterClockwi -> &CounterClockwis - new Transition (1262, 1263), // &Cro -> &Cros - new Transition (1263, 1264), // &Cros -> &Cross - new Transition (1266, 1267), // &cro -> &cros - new Transition (1267, 1268), // &cros -> &cross - new Transition (1301, 1305), // &cue -> &cues - new Transition (1318, 1344), // &cup -> &cups - new Transition (1356, 1362), // &curlyeq -> &curlyeqs - new Transition (1425, 2040), // &D -> &Ds - new Transition (1426, 1457), // &Da -> &Das - new Transition (1432, 2044), // &d -> &ds - new Transition (1433, 1454), // &da -> &das - new Transition (1511, 1512), // &ddot -> &ddots - new Transition (1536, 1537), // &dfi -> &dfis - new Transition (1599, 1639), // &di -> &dis - new Transition (1601, 1617), // &diam -> &diams - new Transition (1610, 1612), // &diamond -> &diamonds - new Transition (1654, 1655), // ÷ontime -> ÷ontimes - new Transition (1694, 1724), // &dot -> &dots - new Transition (1716, 1717), // &dotminu -> &dotminus - new Transition (1721, 1722), // &dotplu -> &dotplus - new Transition (1929, 1930), // &downdownarrow -> &downdownarrows - new Transition (2108, 2418), // &E -> &Es - new Transition (2115, 2422), // &e -> &es - new Transition (2116, 2122), // &ea -> &eas - new Transition (2185, 2198), // &eg -> &egs - new Transition (2204, 2222), // &el -> &els - new Transition (2217, 2218), // &elinter -> &elinters - new Transition (2233, 2279), // &em -> &ems - new Transition (2240, 2242), // &empty -> &emptys - new Transition (2290, 2293), // &en -> &ens - new Transition (2312, 2323), // &ep -> &eps - new Transition (2314, 2316), // &epar -> &epars - new Transition (2320, 2321), // &eplu -> &eplus - new Transition (2326, 2327), // &Ep -> &Eps - new Transition (2339, 2350), // &eq -> &eqs - new Transition (2363, 2364), // &eqslantle -> &eqslantles - new Transition (2364, 2365), // &eqslantles -> &eqslantless - new Transition (2374, 2375), // &equal -> &equals - new Transition (2383, 2384), // &eque -> &eques - new Transition (2405, 2406), // &eqvpar -> &eqvpars - new Transition (2462, 2463), // &exi -> &exis - new Transition (2467, 2468), // &Exi -> &Exis - new Transition (2469, 2470), // &Exist -> &Exists - new Transition (2503, 2697), // &f -> &fs - new Transition (2512, 2513), // &fallingdot -> &fallingdots - new Transition (2517, 2693), // &F -> &Fs - new Transition (2601, 2602), // &fltn -> &fltns - new Transition (2648, 2686), // &fra -> &fras - new Transition (2701, 2927), // &g -> &gs - new Transition (2708, 2923), // &G -> &Gs - new Transition (2765, 2781), // &ge -> &ges - new Transition (2771, 2775), // &geq -> &geqs - new Transition (2796, 2797), // &gesle -> &gesles - new Transition (2832, 2849), // &gn -> &gns - new Transition (2879, 2880), // &GreaterEqualLe -> &GreaterEqualLes - new Transition (2880, 2881), // &GreaterEqualLes -> &GreaterEqualLess - new Transition (2902, 2903), // &GreaterLe -> &GreaterLes - new Transition (2903, 2904), // &GreaterLes -> &GreaterLess - new Transition (2961, 2962), // >que -> >ques - new Transition (2965, 2998), // >r -> >rs - new Transition (2983, 2984), // >reqle -> >reqles - new Transition (2984, 2985), // >reqles -> >reqless - new Transition (2989, 2990), // >reqqle -> >reqqles - new Transition (2990, 2991), // >reqqles -> >reqqless - new Transition (2994, 2995), // >rle -> >rles - new Transition (2995, 2996), // >rles -> >rless - new Transition (3014, 3184), // &H -> &Hs - new Transition (3020, 3188), // &h -> &hs - new Transition (3023, 3024), // &hair -> &hairs - new Transition (3077, 3078), // &heart -> &hearts - new Transition (3112, 3113), // &hk -> &hks - new Transition (3193, 3194), // &hsla -> &hslas - new Transition (3236, 3503), // &I -> &Is - new Transition (3243, 3507), // &i -> &is - new Transition (3375, 3376), // &Implie -> &Implies - new Transition (3410, 3411), // &integer -> &integers - new Transition (3424, 3425), // &Inter -> &Inters - new Transition (3445, 3446), // &Invi -> &Invis - new Transition (3460, 3461), // &InvisibleTime -> &InvisibleTimes - new Transition (3499, 3500), // &ique -> &iques - new Transition (3512, 3520), // &isin -> &isins - new Transition (3555, 3590), // &J -> &Js - new Transition (3561, 3594), // &j -> &js - new Transition (3618, 3684), // &K -> &Ks - new Transition (3624, 3688), // &k -> &ks - new Transition (3692, 4652), // &l -> &ls - new Transition (3698, 4658), // &L -> &Ls - new Transition (3766, 3785), // &larr -> &larrs - new Transition (3770, 3771), // &larrbf -> &larrbfs - new Transition (3773, 3774), // &larrf -> &larrfs - new Transition (3803, 3805), // &late -> &lates - new Transition (3828, 3831), // &lbrk -> &lbrks - new Transition (3869, 3891), // &ld -> &lds - new Transition (3885, 3886), // &ldru -> &ldrus - new Transition (3896, 4197), // &le -> &les - new Transition (3898, 4238), // &Le -> &Les - new Transition (4027, 4028), // &leftleftarrow -> &leftleftarrows - new Transition (4056, 4074), // &leftright -> &leftrights - new Transition (4061, 4063), // &leftrightarrow -> &leftrightarrows - new Transition (4071, 4072), // &leftrightharpoon -> &leftrightharpoons - new Transition (4117, 4118), // &leftthreetime -> &leftthreetimes - new Transition (4187, 4191), // &leq -> &leqs - new Transition (4197, 4215), // &les -> &less - new Transition (4212, 4213), // &lesge -> &lesges - new Transition (4215, 4280), // &less -> &lesss - new Transition (4238, 4239), // &Les -> &Less - new Transition (4276, 4277), // &LessLe -> &LessLes - new Transition (4277, 4278), // &LessLes -> &LessLess - new Transition (4302, 4303), // &lfi -> &lfis - new Transition (4392, 4393), // &lmou -> &lmous - new Transition (4401, 4418), // &ln -> &lns - new Transition (4504, 4505), // &longmap -> &longmaps - new Transition (4570, 4571), // &loplu -> &loplus - new Transition (4576, 4577), // &lotime -> &lotimes - new Transition (4580, 4581), // &lowa -> &lowas - new Transition (4717, 4718), // <ime -> <imes - new Transition (4727, 4728), // <que -> <ques - new Transition (4744, 4745), // &lurd -> &lurds - new Transition (4767, 4941), // &m -> &ms - new Transition (4777, 4778), // &malte -> &maltes - new Transition (4781, 4937), // &M -> &Ms - new Transition (4785, 4787), // &map -> &maps - new Transition (4821, 4822), // &mda -> &mdas - new Transition (4831, 4832), // &mea -> &meas - new Transition (4878, 4879), // &mida -> &midas - new Transition (4891, 4892), // &minu -> &minus - new Transition (4902, 4903), // &Minu -> &Minus - new Transition (4906, 4907), // &MinusPlu -> &MinusPlus - new Transition (4919, 4920), // &mnplu -> &mnplus - new Transition (4925, 4926), // &model -> &models - new Transition (4947, 4948), // &mstpo -> &mstpos - new Transition (4965, 5895), // &n -> &ns - new Transition (4971, 5904), // &N -> &Ns - new Transition (4993, 4994), // &napo -> &napos - new Transition (5006, 5008), // &natural -> &naturals - new Transition (5010, 5011), // &nb -> &nbs - new Transition (5060, 5061), // &nda -> &ndas - new Transition (5064, 5140), // &ne -> &nes - new Transition (5084, 5148), // &Ne -> &Nes - new Transition (5168, 5169), // &NestedLe -> &NestedLes - new Transition (5169, 5170), // &NestedLes -> &NestedLess - new Transition (5172, 5173), // &NestedLessLe -> &NestedLessLes - new Transition (5173, 5174), // &NestedLessLes -> &NestedLessLess - new Transition (5183, 5184), // &nexi -> &nexis - new Transition (5185, 5187), // &nexist -> &nexists - new Transition (5195, 5215), // &ng -> &ngs - new Transition (5198, 5210), // &nge -> &nges - new Transition (5200, 5204), // &ngeq -> &ngeqs - new Transition (5240, 5242), // &ni -> &nis - new Transition (5256, 5328), // &nl -> &nls - new Transition (5270, 5322), // &nle -> &nles - new Transition (5312, 5316), // &nleq -> &nleqs - new Transition (5322, 5324), // &nles -> &nless - new Transition (5434, 5435), // &NotExi -> &NotExis - new Transition (5436, 5437), // &NotExist -> &NotExists - new Transition (5472, 5473), // &NotGreaterLe -> &NotGreaterLes - new Transition (5473, 5474), // &NotGreaterLes -> &NotGreaterLess - new Transition (5529, 5551), // &NotLe -> &NotLes - new Transition (5551, 5552), // &NotLes -> &NotLess - new Transition (5569, 5570), // &NotLessLe -> &NotLessLes - new Transition (5570, 5571), // &NotLessLes -> &NotLessLess - new Transition (5591, 5592), // &NotNe -> &NotNes - new Transition (5612, 5613), // &NotNestedLe -> &NotNestedLes - new Transition (5613, 5614), // &NotNestedLes -> &NotNestedLess - new Transition (5616, 5617), // &NotNestedLessLe -> &NotNestedLessLes - new Transition (5617, 5618), // &NotNestedLessLes -> &NotNestedLessLess - new Transition (5636, 5637), // &NotPrecede -> &NotPrecedes - new Transition (5660, 5661), // &NotRever -> &NotRevers - new Transition (5702, 5703), // &NotSquareSub -> &NotSquareSubs - new Transition (5715, 5716), // &NotSquareSuper -> &NotSquareSupers - new Transition (5727, 5728), // &NotSub -> &NotSubs - new Transition (5742, 5743), // &NotSucceed -> &NotSucceeds - new Transition (5770, 5771), // &NotSuper -> &NotSupers - new Transition (5823, 5831), // &npar -> &npars - new Transition (5942, 5943), // &nsq -> &nsqs - new Transition (5952, 5958), // &nsub -> &nsubs - new Transition (5973, 5979), // &nsup -> &nsups - new Transition (6034, 6040), // &num -> &nums - new Transition (6043, 6107), // &nv -> &nvs - new Transition (6049, 6050), // &nVDa -> &nVDas - new Transition (6054, 6055), // &nVda -> &nVdas - new Transition (6059, 6060), // &nvDa -> &nvDas - new Transition (6064, 6065), // &nvda -> &nvdas - new Transition (6131, 6378), // &O -> &Os - new Transition (6138, 6382), // &o -> &os - new Transition (6139, 6145), // &oa -> &oas - new Transition (6163, 6185), // &od -> &ods - new Transition (6164, 6165), // &oda -> &odas - new Transition (6248, 6249), // &olcro -> &olcros - new Transition (6249, 6250), // &olcros -> &olcross - new Transition (6291, 6292), // &ominu -> &ominus - new Transition (6337, 6338), // &oplu -> &oplus - new Transition (6342, 6368), // &or -> &ors - new Transition (6387, 6388), // &Osla -> &Oslas - new Transition (6392, 6393), // &osla -> &oslas - new Transition (6412, 6413), // &Otime -> &Otimes - new Transition (6416, 6417), // &otime -> &otimes - new Transition (6419, 6420), // &otimesa -> &otimesas - new Transition (6458, 6459), // &OverParenthe -> &OverParenthes - new Transition (6460, 6461), // &OverParenthesi -> &OverParenthesis - new Transition (6463, 6799), // &p -> &ps - new Transition (6465, 6474), // &par -> &pars - new Transition (6482, 6795), // &P -> &Ps - new Transition (6566, 6567), // &plu -> &plus - new Transition (6567, 6599), // &plus -> &pluss - new Transition (6588, 6589), // &Plu -> &Plus - new Transition (6593, 6594), // &PlusMinu -> &PlusMinus - new Transition (6642, 6786), // &pr -> &prs - new Transition (6655, 6721), // &prec -> &precs - new Transition (6676, 6677), // &Precede -> &Precedes - new Transition (6705, 6717), // &precn -> &precns - new Transition (6731, 6733), // &prime -> &primes - new Transition (6735, 6741), // &prn -> &prns - new Transition (6754, 6765), // &prof -> &profs - new Transition (6809, 6810), // &punc -> &puncs - new Transition (6813, 6839), // &Q -> &Qs - new Transition (6817, 6843), // &q -> &qs - new Transition (6855, 6856), // &quaternion -> &quaternions - new Transition (6862, 6863), // &que -> &ques - new Transition (6876, 7542), // &r -> &rs - new Transition (6886, 7548), // &R -> &Rs - new Transition (6932, 6956), // &rarr -> &rarrs - new Transition (6939, 6940), // &rarrbf -> &rarrbfs - new Transition (6944, 6945), // &rarrf -> &rarrfs - new Transition (6983, 6984), // &rational -> &rationals - new Transition (7012, 7015), // &rbrk -> &rbrks - new Transition (7053, 7069), // &rd -> &rds - new Transition (7076, 7087), // &real -> &reals - new Transition (7099, 7100), // &Rever -> &Revers - new Transition (7136, 7137), // &rfi -> &rfis - new Transition (7199, 7431), // &ri -> &ris - new Transition (7202, 7326), // &right -> &rights - new Transition (7302, 7303), // &rightleftarrow -> &rightleftarrows - new Transition (7311, 7312), // &rightleftharpoon -> &rightleftharpoons - new Transition (7323, 7324), // &rightrightarrow -> &rightrightarrows - new Transition (7362, 7363), // &rightthreetime -> &rightthreetimes - new Transition (7437, 7438), // &risingdot -> &risingdots - new Transition (7455, 7456), // &rmou -> &rmous - new Transition (7492, 7493), // &roplu -> &roplus - new Transition (7498, 7499), // &rotime -> &rotimes - new Transition (7509, 7510), // &RoundImplie -> &RoundImplies - new Transition (7575, 7576), // &rtime -> &rtimes - new Transition (7610, 8073), // &S -> &Ss - new Transition (7617, 8077), // &s -> &ss - new Transition (7631, 7687), // &sc -> &scs - new Transition (7670, 7676), // &scn -> &scns - new Transition (7703, 7724), // &se -> &ses - new Transition (7733, 7734), // &setminu -> &setminus - new Transition (7870, 7871), // &simplu -> &simplus - new Transition (7895, 7907), // &sma -> &smas - new Transition (7897, 7898), // &small -> &smalls - new Transition (7904, 7905), // &smallsetminu -> &smallsetminus - new Transition (7914, 7915), // &smepar -> &smepars - new Transition (7926, 7928), // &smte -> &smtes - new Transition (7959, 7960), // &spade -> &spades - new Transition (7968, 7984), // &sq -> &sqs - new Transition (7971, 7973), // &sqcap -> &sqcaps - new Transition (7976, 7978), // &sqcup -> &sqcups - new Transition (7986, 7990), // &sqsub -> &sqsubs - new Transition (7997, 8001), // &sqsup -> &sqsups - new Transition (8023, 8024), // &SquareInter -> &SquareInters - new Transition (8034, 8035), // &SquareSub -> &SquareSubs - new Transition (8047, 8048), // &SquareSuper -> &SquareSupers - new Transition (8113, 8114), // &straightep -> &straighteps - new Transition (8124, 8125), // &strn -> &strns - new Transition (8128, 8165), // &Sub -> &Subs - new Transition (8131, 8169), // &sub -> &subs - new Transition (8157, 8158), // &subplu -> &subplus - new Transition (8199, 8265), // &succ -> &succs - new Transition (8220, 8221), // &Succeed -> &Succeeds - new Transition (8249, 8261), // &succn -> &succns - new Transition (8282, 8348), // &Sup -> &Sups - new Transition (8284, 8352), // &sup -> &sups - new Transition (8292, 8296), // &supd -> &supds - new Transition (8309, 8310), // &Super -> &Supers - new Transition (8320, 8321), // &suph -> &suphs - new Transition (8345, 8346), // &supplu -> &supplus - new Transition (8400, 8705), // &T -> &Ts - new Transition (8404, 8709), // &t -> &ts - new Transition (8485, 8487), // &theta -> &thetas - new Transition (8495, 8503), // &thick -> &thicks - new Transition (8516, 8517), // &thin -> &thins - new Transition (8527, 8531), // &thk -> &thks - new Transition (8577, 8578), // &time -> × - new Transition (8590, 8614), // &to -> &tos - new Transition (8633, 8690), // &tri -> &tris - new Transition (8673, 8674), // &triminu -> &triminus - new Transition (8687, 8688), // &triplu -> &triplus - new Transition (8768, 9153), // &U -> &Us - new Transition (8775, 9157), // &u -> &us - new Transition (8850, 8851), // &ufi -> &ufis - new Transition (8940, 8941), // &UnderParenthe -> &UnderParenthes - new Transition (8942, 8943), // &UnderParenthesi -> &UnderParenthesis - new Transition (8951, 8952), // &UnionPlu -> &UnionPlus - new Transition (8970, 9092), // &Up -> &Ups - new Transition (8983, 9095), // &up -> &ups - new Transition (9065, 9066), // &uplu -> &uplus - new Transition (9124, 9125), // &upuparrow -> &upuparrows - new Transition (9201, 9454), // &v -> &vs - new Transition (9208, 9252), // &var -> &vars - new Transition (9210, 9211), // &varep -> &vareps - new Transition (9259, 9260), // &varsub -> &varsubs - new Transition (9269, 9270), // &varsup -> &varsups - new Transition (9303, 9450), // &V -> &Vs - new Transition (9321, 9322), // &VDa -> &VDas - new Transition (9326, 9327), // &Vda -> &Vdas - new Transition (9331, 9332), // &vDa -> &vDas - new Transition (9336, 9337), // &vda -> &vdas - new Transition (9425, 9426), // &vn -> &vns - new Transition (9473, 9474), // &Vvda -> &Vvdas - new Transition (9484, 9540), // &W -> &Ws - new Transition (9490, 9544), // &w -> &ws - new Transition (9548, 9636), // &x -> &xs - new Transition (9565, 9632), // &X -> &Xs - new Transition (9599, 9600), // &xni -> &xnis - new Transition (9615, 9616), // &xoplu -> &xoplus - new Transition (9648, 9649), // &xuplu -> &xuplus - new Transition (9665, 9724), // &Y -> &Ys - new Transition (9672, 9728), // &y -> &ys - new Transition (9747, 9840), // &Z -> &Zs - new Transition (9754, 9844) // &z -> &zs - }; - TransitionTable_t = new Transition[499] { - new Transition (0, 8404), // & -> &t - new Transition (1, 269), // &A -> &At - new Transition (4, 5), // &Aacu -> &Aacut - new Transition (8, 275), // &a -> &at - new Transition (11, 12), // &aacu -> &aacut - new Transition (42, 43), // &acu -> &acut - new Transition (164, 165), // &angr -> &angrt - new Transition (172, 176), // &angs -> &angst - new Transition (223, 224), // &ApplyFunc -> &ApplyFunct - new Transition (251, 260), // &as -> &ast - new Transition (294, 295), // &awconin -> &awconint - new Transition (298, 299), // &awin -> &awint - new Transition (362, 364), // &bbrk -> &bbrkt - new Transition (384, 426), // &be -> &bet - new Transition (390, 423), // &Be -> &Bet - new Transition (400, 401), // &bemp -> &bempt - new Transition (443, 481), // &big -> &bigt - new Transition (455, 465), // &bigo -> &bigot - new Transition (457, 458), // &bigodo -> &bigodot - new Transition (471, 477), // &bigs -> &bigst - new Transition (522, 538), // &black -> &blackt - new Transition (554, 555), // &blacktrianglelef -> &blacktriangleleft - new Transition (560, 561), // &blacktrianglerigh -> &blacktriangleright - new Transition (588, 589), // &bNo -> &bNot - new Transition (591, 592), // &bno -> &bnot - new Transition (598, 602), // &bo -> &bot - new Transition (602, 604), // &bot -> &bott - new Transition (608, 609), // &bow -> &bowt - new Transition (613, 667), // &box -> &boxt - new Transition (771, 772), // &bulle -> &bullet - new Transition (792, 793), // &Cacu -> &Cacut - new Transition (796, 1287), // &c -> &ct - new Transition (799, 800), // &cacu -> &cacut - new Transition (825, 826), // &capdo -> &capdot - new Transition (828, 829), // &Capi -> &Capit - new Transition (839, 840), // &CapitalDifferen -> &CapitalDifferent - new Transition (849, 850), // &care -> &caret - new Transition (897, 898), // &Cconin -> &Cconint - new Transition (908, 909), // &Cdo -> &Cdot - new Transition (912, 913), // &cdo -> &cdot - new Transition (928, 929), // &cemp -> &cempt - new Transition (933, 934), // &cen -> ¢ - new Transition (936, 937), // &Cen -> &Cent - new Transition (941, 942), // &CenterDo -> &CenterDot - new Transition (947, 948), // ¢erdo -> ¢erdot - new Transition (995, 996), // &circlearrowlef -> &circlearrowleft - new Transition (1001, 1002), // &circlearrowrigh -> &circlearrowright - new Transition (1006, 1007), // &circledas -> &circledast - new Transition (1025, 1026), // &CircleDo -> &CircleDot - new Transition (1056, 1057), // &cirfnin -> &cirfnint - new Transition (1078, 1079), // &ClockwiseCon -> &ClockwiseCont - new Transition (1084, 1085), // &ClockwiseContourIn -> &ClockwiseContourInt - new Transition (1107, 1108), // &CloseCurlyDoubleQuo -> &CloseCurlyDoubleQuot - new Transition (1113, 1114), // &CloseCurlyQuo -> &CloseCurlyQuot - new Transition (1123, 1124), // &clubsui -> &clubsuit - new Transition (1144, 1146), // &comma -> &commat - new Transition (1157, 1158), // &complemen -> &complement - new Transition (1168, 1169), // &congdo -> &congdot - new Transition (1171, 1187), // &Con -> &Cont - new Transition (1176, 1177), // &Congruen -> &Congruent - new Transition (1180, 1181), // &Conin -> &Conint - new Transition (1184, 1185), // &conin -> &conint - new Transition (1192, 1193), // &ContourIn -> &ContourInt - new Transition (1214, 1215), // &Coproduc -> &Coproduct - new Transition (1227, 1228), // &Coun -> &Count - new Transition (1242, 1243), // &CounterClockwiseCon -> &CounterClockwiseCont - new Transition (1248, 1249), // &CounterClockwiseContourIn -> &CounterClockwiseContourInt - new Transition (1289, 1290), // &ctdo -> &ctdot - new Transition (1338, 1339), // &cupdo -> &cupdot - new Transition (1390, 1391), // &curvearrowlef -> &curvearrowleft - new Transition (1396, 1397), // &curvearrowrigh -> &curvearrowright - new Transition (1412, 1413), // &cwconin -> &cwconint - new Transition (1416, 1417), // &cwin -> &cwint - new Transition (1421, 1422), // &cylc -> &cylct - new Transition (1432, 2067), // &d -> &dt - new Transition (1440, 1441), // &dale -> &dalet - new Transition (1503, 1504), // &DDo -> &DDot - new Transition (1510, 1511), // &ddo -> &ddot - new Transition (1520, 1522), // &Del -> &Delt - new Transition (1525, 1526), // &del -> &delt - new Transition (1530, 1531), // &demp -> &dempt - new Transition (1538, 1539), // &dfish -> &dfisht - new Transition (1561, 1562), // &Diacri -> &Diacrit - new Transition (1569, 1570), // &DiacriticalAcu -> &DiacriticalAcut - new Transition (1574, 1575), // &DiacriticalDo -> &DiacriticalDot - new Transition (1583, 1584), // &DiacriticalDoubleAcu -> &DiacriticalDoubleAcut - new Transition (1614, 1615), // &diamondsui -> &diamondsuit - new Transition (1626, 1627), // &Differen -> &Different - new Transition (1650, 1651), // ÷on -> ÷ont - new Transition (1679, 1694), // &do -> &dot - new Transition (1685, 1692), // &Do -> &Dot - new Transition (1697, 1698), // &DotDo -> &DotDot - new Transition (1704, 1705), // &doteqdo -> &doteqdot - new Transition (1750, 1751), // &DoubleCon -> &DoubleCont - new Transition (1756, 1757), // &DoubleContourIn -> &DoubleContourInt - new Transition (1765, 1766), // &DoubleDo -> &DoubleDot - new Transition (1778, 1779), // &DoubleLef -> &DoubleLeft - new Transition (1789, 1790), // &DoubleLeftRigh -> &DoubleLeftRight - new Transition (1806, 1807), // &DoubleLongLef -> &DoubleLongLeft - new Transition (1817, 1818), // &DoubleLongLeftRigh -> &DoubleLongLeftRight - new Transition (1828, 1829), // &DoubleLongRigh -> &DoubleLongRight - new Transition (1839, 1840), // &DoubleRigh -> &DoubleRight - new Transition (1871, 1872), // &DoubleVer -> &DoubleVert - new Transition (1941, 1942), // &downharpoonlef -> &downharpoonleft - new Transition (1947, 1948), // &downharpoonrigh -> &downharpoonright - new Transition (1952, 1953), // &DownLef -> &DownLeft - new Transition (1957, 1958), // &DownLeftRigh -> &DownLeftRight - new Transition (1961, 1962), // &DownLeftRightVec -> &DownLeftRightVect - new Transition (1971, 1972), // &DownLeftTeeVec -> &DownLeftTeeVect - new Transition (1978, 1979), // &DownLeftVec -> &DownLeftVect - new Transition (1990, 1991), // &DownRigh -> &DownRight - new Transition (1997, 1998), // &DownRightTeeVec -> &DownRightTeeVect - new Transition (2004, 2005), // &DownRightVec -> &DownRightVect - new Transition (2040, 2057), // &Ds -> &Dst - new Transition (2044, 2062), // &ds -> &dst - new Transition (2069, 2070), // &dtdo -> &dtdot - new Transition (2108, 2436), // &E -> &Et - new Transition (2111, 2112), // &Eacu -> &Eacut - new Transition (2115, 2439), // &e -> &et - new Transition (2118, 2119), // &eacu -> &eacut - new Transition (2122, 2123), // &eas -> &east - new Transition (2159, 2160), // &eDDo -> &eDDot - new Transition (2163, 2164), // &Edo -> &Edot - new Transition (2166, 2167), // &eDo -> &eDot - new Transition (2170, 2171), // &edo -> &edot - new Transition (2177, 2178), // &efDo -> &efDot - new Transition (2201, 2202), // &egsdo -> &egsdot - new Transition (2210, 2211), // &Elemen -> &Element - new Transition (2214, 2215), // &elin -> &elint - new Transition (2225, 2226), // &elsdo -> &elsdot - new Transition (2238, 2239), // &emp -> &empt - new Transition (2243, 2244), // &emptyse -> &emptyset - new Transition (2246, 2247), // &Emp -> &Empt - new Transition (2356, 2357), // &eqslan -> &eqslant - new Transition (2358, 2359), // &eqslantg -> &eqslantgt - new Transition (2384, 2385), // &eques -> &equest - new Transition (2415, 2416), // &erDo -> &erDot - new Transition (2427, 2428), // &esdo -> &esdot - new Transition (2463, 2464), // &exis -> &exist - new Transition (2468, 2469), // &Exis -> &Exist - new Transition (2474, 2475), // &expec -> &expect - new Transition (2476, 2477), // &expecta -> &expectat - new Transition (2486, 2487), // &Exponen -> &Exponent - new Transition (2496, 2497), // &exponen -> &exponent - new Transition (2511, 2512), // &fallingdo -> &fallingdot - new Transition (2592, 2600), // &fl -> &flt - new Transition (2593, 2594), // &fla -> &flat - new Transition (2634, 2635), // &Fourier -> &Fouriert - new Transition (2641, 2642), // &fpar -> &fpart - new Transition (2644, 2645), // &fpartin -> &fpartint - new Transition (2701, 2942), // &g -> > - new Transition (2704, 2705), // &gacu -> &gacut - new Transition (2708, 2940), // &G -> &Gt - new Transition (2756, 2757), // &Gdo -> &Gdot - new Transition (2760, 2761), // &gdo -> &gdot - new Transition (2778, 2779), // &geqslan -> &geqslant - new Transition (2787, 2788), // &gesdo -> &gesdot - new Transition (2868, 2869), // &Grea -> &Great - new Transition (2896, 2897), // &GreaterGrea -> &GreaterGreat - new Transition (2909, 2910), // &GreaterSlan -> &GreaterSlant - new Transition (2951, 2952), // >do -> >dot - new Transition (2962, 2963), // >ques -> >quest - new Transition (2977, 2978), // >rdo -> >rdot - new Transition (3004, 3005), // &gver -> &gvert - new Transition (3015, 3058), // &Ha -> &Hat - new Transition (3032, 3033), // &hamil -> &hamilt - new Transition (3076, 3077), // &hear -> &heart - new Transition (3081, 3082), // &heartsui -> &heartsuit - new Transition (3104, 3105), // &Hilber -> &Hilbert - new Transition (3131, 3132), // &hom -> &homt - new Transition (3133, 3134), // &homth -> &homtht - new Transition (3140, 3141), // &hooklef -> &hookleft - new Transition (3151, 3152), // &hookrigh -> &hookright - new Transition (3175, 3176), // &Horizon -> &Horizont - new Transition (3184, 3197), // &Hs -> &Hst - new Transition (3188, 3202), // &hs -> &hst - new Transition (3236, 3528), // &I -> &It - new Transition (3239, 3240), // &Iacu -> &Iacut - new Transition (3243, 3526), // &i -> &it - new Transition (3246, 3247), // &iacu -> &iacut - new Transition (3266, 3267), // &Ido -> &Idot - new Transition (3305, 3306), // &iiiin -> &iiiint - new Transition (3308, 3309), // &iiin -> &iiint - new Transition (3316, 3317), // &iio -> &iiot - new Transition (3337, 3362), // &ima -> &imat - new Transition (3359, 3360), // &imagpar -> &imagpart - new Transition (3378, 3401), // &in -> &int - new Transition (3387, 3389), // &infin -> &infint - new Transition (3395, 3396), // &inodo -> &inodot - new Transition (3398, 3399), // &In -> &Int - new Transition (3427, 3428), // &Intersec -> &Intersect - new Transition (3467, 3489), // &io -> &iot - new Transition (3471, 3486), // &Io -> &Iot - new Transition (3500, 3501), // &iques -> ¿ - new Transition (3515, 3516), // &isindo -> &isindot - new Transition (3578, 3579), // &jma -> &jmat - new Transition (3692, 4698), // &l -> < - new Transition (3693, 3794), // &lA -> &lAt - new Transition (3698, 4696), // &L -> &Lt - new Transition (3701, 3702), // &Lacu -> &Lacut - new Transition (3705, 3792), // &la -> &lat - new Transition (3707, 3708), // &lacu -> &lacut - new Transition (3713, 3714), // &laemp -> &laempt - new Transition (3750, 3751), // &Laplace -> &Laplacet - new Transition (3766, 3789), // &larr -> &larrt - new Transition (3899, 3900), // &Lef -> &Left - new Transition (3911, 3912), // &LeftAngleBracke -> &LeftAngleBracket - new Transition (3925, 3926), // &lef -> &left - new Transition (3926, 4109), // &left -> &leftt - new Transition (3931, 3948), // &leftarrow -> &leftarrowt - new Transition (3940, 3941), // &LeftArrowRigh -> &LeftArrowRight - new Transition (3972, 3973), // &LeftDoubleBracke -> &LeftDoubleBracket - new Transition (3982, 3983), // &LeftDownTeeVec -> &LeftDownTeeVect - new Transition (3989, 3990), // &LeftDownVec -> &LeftDownVect - new Transition (4021, 4022), // &leftlef -> &leftleft - new Transition (4033, 4034), // &LeftRigh -> &LeftRight - new Transition (4044, 4045), // &Leftrigh -> &Leftright - new Transition (4055, 4056), // &leftrigh -> &leftright - new Transition (4087, 4088), // &LeftRightVec -> &LeftRightVect - new Transition (4104, 4105), // &LeftTeeVec -> &LeftTeeVect - new Transition (4113, 4114), // &leftthree -> &leftthreet - new Transition (4146, 4147), // &LeftUpDownVec -> &LeftUpDownVect - new Transition (4156, 4157), // &LeftUpTeeVec -> &LeftUpTeeVect - new Transition (4163, 4164), // &LeftUpVec -> &LeftUpVect - new Transition (4174, 4175), // &LeftVec -> &LeftVect - new Transition (4194, 4195), // &leqslan -> &leqslant - new Transition (4203, 4204), // &lesdo -> &lesdot - new Transition (4224, 4225), // &lessdo -> &lessdot - new Transition (4229, 4230), // &lesseqg -> &lesseqgt - new Transition (4234, 4235), // &lesseqqg -> &lesseqqgt - new Transition (4248, 4249), // &LessEqualGrea -> &LessEqualGreat - new Transition (4266, 4267), // &LessGrea -> &LessGreat - new Transition (4271, 4272), // &lessg -> &lessgt - new Transition (4287, 4288), // &LessSlan -> &LessSlant - new Transition (4304, 4305), // &lfish -> &lfisht - new Transition (4348, 4375), // &ll -> &llt - new Transition (4362, 4363), // &Llef -> &Lleft - new Transition (4382, 4383), // &Lmido -> &Lmidot - new Transition (4388, 4389), // &lmido -> &lmidot - new Transition (4393, 4394), // &lmous -> &lmoust - new Transition (4422, 4573), // &lo -> &lot - new Transition (4439, 4440), // &LongLef -> &LongLeft - new Transition (4449, 4450), // &Longlef -> &Longleft - new Transition (4461, 4462), // &longlef -> &longleft - new Transition (4472, 4473), // &LongLeftRigh -> &LongLeftRight - new Transition (4483, 4484), // &Longleftrigh -> &Longleftright - new Transition (4494, 4495), // &longleftrigh -> &longleftright - new Transition (4505, 4506), // &longmaps -> &longmapst - new Transition (4512, 4513), // &LongRigh -> &LongRight - new Transition (4523, 4524), // &Longrigh -> &Longright - new Transition (4534, 4535), // &longrigh -> &longright - new Transition (4551, 4552), // &looparrowlef -> &looparrowleft - new Transition (4557, 4558), // &looparrowrigh -> &looparrowright - new Transition (4581, 4582), // &lowas -> &lowast - new Transition (4593, 4594), // &LowerLef -> &LowerLeft - new Transition (4604, 4605), // &LowerRigh -> &LowerRight - new Transition (4625, 4626), // &lparl -> &lparlt - new Transition (4628, 4648), // &lr -> &lrt - new Transition (4652, 4689), // &ls -> &lst - new Transition (4658, 4684), // &Ls -> &Lst - new Transition (4707, 4708), // <do -> <dot - new Transition (4728, 4729), // <ques -> <quest - new Transition (4757, 4758), // &lver -> &lvert - new Transition (4772, 4775), // &mal -> &malt - new Transition (4787, 4788), // &maps -> &mapst - new Transition (4798, 4799), // &mapstolef -> &mapstoleft - new Transition (4827, 4828), // &mDDo -> &mDDot - new Transition (4857, 4858), // &Mellin -> &Mellint - new Transition (4879, 4880), // &midas -> &midast - new Transition (4887, 4888), // &middo -> · - new Transition (4941, 4945), // &ms -> &mst - new Transition (4954, 4955), // &mul -> &mult - new Transition (4965, 5988), // &n -> &nt - new Transition (4966, 5001), // &na -> &nat - new Transition (4971, 5992), // &N -> &Nt - new Transition (4974, 4975), // &Nacu -> &Nacut - new Transition (4979, 4980), // &nacu -> &nacut - new Transition (5049, 5050), // &ncongdo -> &ncongdot - new Transition (5081, 5082), // &nedo -> &nedot - new Transition (5086, 5087), // &Nega -> &Negat - new Transition (5148, 5149), // &Nes -> &Nest - new Transition (5155, 5156), // &NestedGrea -> &NestedGreat - new Transition (5162, 5163), // &NestedGreaterGrea -> &NestedGreaterGreat - new Transition (5184, 5185), // &nexis -> &nexist - new Transition (5195, 5221), // &ng -> &ngt - new Transition (5207, 5208), // &ngeqslan -> &ngeqslant - new Transition (5212, 5219), // &nG -> &nGt - new Transition (5256, 5334), // &nl -> &nlt - new Transition (5272, 5332), // &nL -> &nLt - new Transition (5274, 5275), // &nLef -> &nLeft - new Transition (5282, 5283), // &nlef -> &nleft - new Transition (5293, 5294), // &nLeftrigh -> &nLeftright - new Transition (5304, 5305), // &nleftrigh -> &nleftright - new Transition (5319, 5320), // &nleqslan -> &nleqslant - new Transition (5347, 5376), // &No -> &Not - new Transition (5372, 5378), // &no -> ¬ - new Transition (5387, 5388), // &NotCongruen -> &NotCongruent - new Transition (5404, 5405), // &NotDoubleVer -> &NotDoubleVert - new Transition (5419, 5420), // &NotElemen -> &NotElement - new Transition (5435, 5436), // &NotExis -> &NotExist - new Transition (5442, 5443), // &NotGrea -> &NotGreat - new Transition (5466, 5467), // &NotGreaterGrea -> &NotGreaterGreat - new Transition (5479, 5480), // &NotGreaterSlan -> &NotGreaterSlant - new Transition (5516, 5517), // ¬indo -> ¬indot - new Transition (5530, 5531), // &NotLef -> &NotLeft - new Transition (5563, 5564), // &NotLessGrea -> &NotLessGreat - new Transition (5576, 5577), // &NotLessSlan -> &NotLessSlant - new Transition (5592, 5593), // &NotNes -> &NotNest - new Transition (5599, 5600), // &NotNestedGrea -> &NotNestedGreat - new Transition (5606, 5607), // &NotNestedGreaterGrea -> &NotNestedGreaterGreat - new Transition (5648, 5649), // &NotPrecedesSlan -> &NotPrecedesSlant - new Transition (5668, 5669), // &NotReverseElemen -> &NotReverseElement - new Transition (5673, 5674), // &NotRigh -> &NotRight - new Transition (5704, 5705), // &NotSquareSubse -> &NotSquareSubset - new Transition (5717, 5718), // &NotSquareSuperse -> &NotSquareSuperset - new Transition (5729, 5730), // &NotSubse -> &NotSubset - new Transition (5754, 5755), // &NotSucceedsSlan -> &NotSucceedsSlant - new Transition (5772, 5773), // &NotSuperse -> &NotSuperset - new Transition (5811, 5812), // &NotVer -> &NotVert - new Transition (5823, 5834), // &npar -> &npart - new Transition (5839, 5840), // &npolin -> &npolint - new Transition (5855, 5889), // &nr -> &nrt - new Transition (5871, 5872), // &nRigh -> &nRight - new Transition (5881, 5882), // &nrigh -> &nright - new Transition (5912, 5913), // &nshor -> &nshort - new Transition (5959, 5960), // &nsubse -> &nsubset - new Transition (5980, 5981), // &nsupse -> &nsupset - new Transition (6015, 6016), // &ntrianglelef -> &ntriangleleft - new Transition (6024, 6025), // &ntrianglerigh -> &ntriangleright - new Transition (6068, 6071), // &nvg -> &nvgt - new Transition (6084, 6091), // &nvl -> &nvlt - new Transition (6097, 6102), // &nvr -> &nvrt - new Transition (6131, 6399), // &O -> &Ot - new Transition (6134, 6135), // &Oacu -> &Oacut - new Transition (6138, 6405), // &o -> &ot - new Transition (6141, 6142), // &oacu -> &oacut - new Transition (6145, 6146), // &oas -> &oast - new Transition (6182, 6183), // &odo -> &odot - new Transition (6210, 6225), // &og -> &ogt - new Transition (6235, 6236), // &oin -> &oint - new Transition (6238, 6256), // &ol -> &olt - new Transition (6322, 6323), // &OpenCurlyDoubleQuo -> &OpenCurlyDoubleQuot - new Transition (6328, 6329), // &OpenCurlyQuo -> &OpenCurlyQuot - new Transition (6448, 6449), // &OverBracke -> &OverBracket - new Transition (6455, 6456), // &OverParen -> &OverParent - new Transition (6465, 6480), // &par -> &part - new Transition (6484, 6485), // &Par -> &Part - new Transition (6498, 6513), // &per -> &pert - new Transition (6500, 6501), // &percn -> &percnt - new Transition (6534, 6535), // &phmma -> &phmmat - new Transition (6543, 6545), // &pi -> &pit - new Transition (6567, 6603), // &plus -> &plust - new Transition (6624, 6625), // &poin -> &point - new Transition (6627, 6628), // &pointin -> &pointint - new Transition (6688, 6689), // &PrecedesSlan -> &PrecedesSlant - new Transition (6751, 6752), // &Produc -> &Product - new Transition (6770, 6783), // &prop -> &propt - new Transition (6774, 6775), // &Propor -> &Proport - new Transition (6822, 6823), // &qin -> &qint - new Transition (6848, 6849), // &qua -> &quat - new Transition (6859, 6860), // &quatin -> &quatint - new Transition (6863, 6864), // &ques -> &quest - new Transition (6873, 6874), // &quo -> " - new Transition (6876, 7567), // &r -> &rt - new Transition (6877, 6968), // &rA -> &rAt - new Transition (6882, 6973), // &ra -> &rat - new Transition (6889, 6890), // &Racu -> &Racut - new Transition (6893, 6894), // &racu -> &racut - new Transition (6903, 6904), // &raemp -> &raempt - new Transition (6926, 6960), // &Rarr -> &Rarrt - new Transition (6932, 6963), // &rarr -> &rarrt - new Transition (7084, 7085), // &realpar -> &realpart - new Transition (7089, 7090), // &rec -> &rect - new Transition (7107, 7108), // &ReverseElemen -> &ReverseElement - new Transition (7138, 7139), // &rfish -> &rfisht - new Transition (7173, 7174), // &Righ -> &Right - new Transition (7185, 7186), // &RightAngleBracke -> &RightAngleBracket - new Transition (7201, 7202), // &righ -> &right - new Transition (7202, 7354), // &right -> &rightt - new Transition (7207, 7223), // &rightarrow -> &rightarrowt - new Transition (7215, 7216), // &RightArrowLef -> &RightArrowLeft - new Transition (7247, 7248), // &RightDoubleBracke -> &RightDoubleBracket - new Transition (7257, 7258), // &RightDownTeeVec -> &RightDownTeeVect - new Transition (7264, 7265), // &RightDownVec -> &RightDownVect - new Transition (7296, 7297), // &rightlef -> &rightleft - new Transition (7317, 7318), // &rightrigh -> &rightright - new Transition (7349, 7350), // &RightTeeVec -> &RightTeeVect - new Transition (7358, 7359), // &rightthree -> &rightthreet - new Transition (7391, 7392), // &RightUpDownVec -> &RightUpDownVect - new Transition (7401, 7402), // &RightUpTeeVec -> &RightUpTeeVect - new Transition (7408, 7409), // &RightUpVec -> &RightUpVect - new Transition (7419, 7420), // &RightVec -> &RightVect - new Transition (7436, 7437), // &risingdo -> &risingdot - new Transition (7456, 7457), // &rmous -> &rmoust - new Transition (7469, 7495), // &ro -> &rot - new Transition (7516, 7517), // &rparg -> &rpargt - new Transition (7523, 7524), // &rppolin -> &rppolint - new Transition (7534, 7535), // &Rrigh -> &Rright - new Transition (7585, 7586), // &rtril -> &rtrilt - new Transition (7610, 8096), // &S -> &St - new Transition (7613, 7614), // &Sacu -> &Sacut - new Transition (7617, 8100), // &s -> &st - new Transition (7620, 7621), // &sacu -> &sacut - new Transition (7684, 7685), // &scpolin -> &scpolint - new Transition (7696, 7697), // &sdo -> &sdot - new Transition (7703, 7729), // &se -> &set - new Transition (7718, 7719), // &sec -> § - new Transition (7738, 7739), // &sex -> &sext - new Transition (7774, 7775), // &Shor -> &Short - new Transition (7788, 7789), // &ShortLef -> &ShortLeft - new Transition (7797, 7798), // &shor -> &short - new Transition (7815, 7816), // &ShortRigh -> &ShortRight - new Transition (7850, 7851), // &simdo -> &simdot - new Transition (7894, 7924), // &sm -> &smt - new Transition (7899, 7900), // &smallse -> &smallset - new Transition (7937, 7938), // &sof -> &soft - new Transition (7963, 7964), // &spadesui -> &spadesuit - new Transition (7981, 7982), // &Sqr -> &Sqrt - new Transition (7991, 7992), // &sqsubse -> &sqsubset - new Transition (8002, 8003), // &sqsupse -> &sqsupset - new Transition (8020, 8021), // &SquareIn -> &SquareInt - new Transition (8026, 8027), // &SquareIntersec -> &SquareIntersect - new Transition (8036, 8037), // &SquareSubse -> &SquareSubset - new Transition (8049, 8050), // &SquareSuperse -> &SquareSuperset - new Transition (8077, 8091), // &ss -> &sst - new Transition (8081, 8082), // &sse -> &sset - new Transition (8110, 8111), // &straigh -> &straight - new Transition (8134, 8135), // &subdo -> &subdot - new Transition (8142, 8143), // &subedo -> &subedot - new Transition (8147, 8148), // &submul -> &submult - new Transition (8166, 8167), // &Subse -> &Subset - new Transition (8170, 8171), // &subse -> &subset - new Transition (8232, 8233), // &SucceedsSlan -> &SucceedsSlant - new Transition (8272, 8273), // &SuchTha -> &SuchThat - new Transition (8293, 8294), // &supdo -> &supdot - new Transition (8305, 8306), // &supedo -> &supedot - new Transition (8311, 8312), // &Superse -> &Superset - new Transition (8335, 8336), // &supmul -> &supmult - new Transition (8349, 8350), // &Supse -> &Supset - new Transition (8353, 8354), // &supse -> &supset - new Transition (8408, 8409), // &targe -> &target - new Transition (8446, 8447), // &tdo -> &tdot - new Transition (8462, 8484), // &the -> &thet - new Transition (8468, 8481), // &The -> &Thet - new Transition (8587, 8588), // &tin -> &tint - new Transition (8597, 8598), // &topbo -> &topbot - new Transition (8633, 8693), // &tri -> &trit - new Transition (8647, 8648), // &trianglelef -> &triangleleft - new Transition (8658, 8659), // &trianglerigh -> &triangleright - new Transition (8665, 8666), // &trido -> &tridot - new Transition (8682, 8683), // &TripleDo -> &TripleDot - new Transition (8705, 8727), // &Ts -> &Tst - new Transition (8709, 8732), // &ts -> &tst - new Transition (8739, 8740), // &twix -> &twixt - new Transition (8749, 8750), // &twoheadlef -> &twoheadleft - new Transition (8760, 8761), // &twoheadrigh -> &twoheadright - new Transition (8768, 9166), // &U -> &Ut - new Transition (8771, 8772), // &Uacu -> &Uacut - new Transition (8775, 9161), // &u -> &ut - new Transition (8778, 8779), // &uacu -> &uacut - new Transition (8852, 8853), // &ufish -> &ufisht - new Transition (8887, 8900), // &ul -> &ult - new Transition (8930, 8931), // &UnderBracke -> &UnderBracket - new Transition (8937, 8938), // &UnderParen -> &UnderParent - new Transition (9055, 9056), // &upharpoonlef -> &upharpoonleft - new Transition (9061, 9062), // &upharpoonrigh -> &upharpoonright - new Transition (9073, 9074), // &UpperLef -> &UpperLeft - new Transition (9084, 9085), // &UpperRigh -> &UpperRight - new Transition (9127, 9149), // &ur -> &urt - new Transition (9163, 9164), // &utdo -> &utdot - new Transition (9205, 9206), // &vangr -> &vangrt - new Transition (9208, 9279), // &var -> &vart - new Transition (9224, 9225), // &varno -> &varnot - new Transition (9239, 9240), // &varprop -> &varpropt - new Transition (9261, 9262), // &varsubse -> &varsubset - new Transition (9271, 9272), // &varsupse -> &varsupset - new Transition (9281, 9282), // &varthe -> &varthet - new Transition (9294, 9295), // &vartrianglelef -> &vartriangleleft - new Transition (9300, 9301), // &vartrianglerigh -> &vartriangleright - new Transition (9360, 9370), // &Ver -> &Vert - new Transition (9365, 9372), // &ver -> &vert - new Transition (9392, 9393), // &VerticalSepara -> &VerticalSeparat - new Transition (9420, 9421), // &vl -> &vlt - new Transition (9445, 9446), // &vr -> &vrt - new Transition (9536, 9537), // &wrea -> &wreat - new Transition (9560, 9561), // &xd -> &xdt - new Transition (9602, 9618), // &xo -> &xot - new Transition (9604, 9605), // &xodo -> &xodot - new Transition (9645, 9651), // &xu -> &xut - new Transition (9668, 9669), // &Yacu -> &Yacut - new Transition (9675, 9676), // &yacu -> &yacut - new Transition (9750, 9751), // &Zacu -> &Zacut - new Transition (9757, 9758), // &zacu -> &zacut - new Transition (9778, 9779), // &Zdo -> &Zdot - new Transition (9782, 9783), // &zdo -> &zdot - new Transition (9785, 9808), // &ze -> &zet - new Transition (9786, 9787), // &zee -> &zeet - new Transition (9791, 9805), // &Ze -> &Zet - new Transition (9796, 9797) // &ZeroWid -> &ZeroWidt - }; - TransitionTable_u = new Transition[278] { - new Transition (0, 8775), // & -> &u - new Transition (1, 281), // &A -> &Au - new Transition (3, 4), // &Aac -> &Aacu - new Transition (8, 285), // &a -> &au - new Transition (10, 11), // &aac -> &aacu - new Transition (27, 42), // &ac -> &acu - new Transition (220, 221), // &ApplyF -> &ApplyFu - new Transition (301, 767), // &b -> &bu - new Transition (331, 781), // &B -> &Bu - new Transition (380, 381), // &bdq -> &bdqu - new Transition (386, 387), // &beca -> &becau - new Transition (392, 393), // &Beca -> &Becau - new Transition (411, 412), // &berno -> &bernou - new Transition (416, 417), // &Berno -> &Bernou - new Transition (443, 497), // &big -> &bigu - new Transition (444, 452), // &bigc -> &bigcu - new Transition (461, 462), // &bigopl -> &bigoplu - new Transition (473, 474), // &bigsqc -> &bigsqcu - new Transition (488, 494), // &bigtriangle -> &bigtriangleu - new Transition (499, 500), // &bigupl -> &biguplu - new Transition (532, 533), // &blacksq -> &blacksqu - new Transition (582, 583), // &bneq -> &bnequ - new Transition (613, 678), // &box -> &boxu - new Transition (636, 650), // &boxH -> &boxHu - new Transition (638, 654), // &boxh -> &boxhu - new Transition (658, 659), // &boxmin -> &boxminu - new Transition (663, 664), // &boxpl -> &boxplu - new Transition (763, 764), // &bsolhs -> &bsolhsu - new Transition (789, 1315), // &C -> &Cu - new Transition (791, 792), // &Cac -> &Cacu - new Transition (796, 1292), // &c -> &cu - new Transition (798, 799), // &cac -> &cacu - new Transition (813, 814), // &capbrc -> &capbrcu - new Transition (817, 821), // &capc -> &capcu - new Transition (861, 900), // &cc -> &ccu - new Transition (1034, 1035), // &CircleMin -> &CircleMinu - new Transition (1039, 1040), // &CirclePl -> &CirclePlu - new Transition (1080, 1081), // &ClockwiseConto -> &ClockwiseContou - new Transition (1094, 1095), // &CloseC -> &CloseCu - new Transition (1100, 1101), // &CloseCurlyDo -> &CloseCurlyDou - new Transition (1105, 1106), // &CloseCurlyDoubleQ -> &CloseCurlyDoubleQu - new Transition (1111, 1112), // &CloseCurlyQ -> &CloseCurlyQu - new Transition (1117, 1118), // &cl -> &clu - new Transition (1120, 1122), // &clubs -> &clubsu - new Transition (1126, 1226), // &Co -> &Cou - new Transition (1173, 1174), // &Congr -> &Congru - new Transition (1188, 1189), // &Conto -> &Contou - new Transition (1212, 1213), // &Coprod -> &Coprodu - new Transition (1244, 1245), // &CounterClockwiseConto -> &CounterClockwiseContou - new Transition (1274, 1278), // &cs -> &csu - new Transition (1330, 1334), // &cupc -> &cupcu - new Transition (1362, 1363), // &curlyeqs -> &curlyeqsu - new Transition (1432, 2077), // &d -> &du - new Transition (1568, 1569), // &DiacriticalAc -> &DiacriticalAcu - new Transition (1574, 1577), // &DiacriticalDo -> &DiacriticalDou - new Transition (1582, 1583), // &DiacriticalDoubleAc -> &DiacriticalDoubleAcu - new Transition (1612, 1613), // &diamonds -> &diamondsu - new Transition (1679, 1731), // &do -> &dou - new Transition (1685, 1744), // &Do -> &Dou - new Transition (1708, 1709), // &DotEq -> &DotEqu - new Transition (1715, 1716), // &dotmin -> &dotminu - new Transition (1720, 1721), // &dotpl -> &dotplu - new Transition (1725, 1726), // &dotsq -> &dotsqu - new Transition (1752, 1753), // &DoubleConto -> &DoubleContou - new Transition (2108, 2447), // &E -> &Eu - new Transition (2110, 2111), // &Eac -> &Eacu - new Transition (2115, 2451), // &e -> &eu - new Transition (2117, 2118), // &eac -> &eacu - new Transition (2255, 2256), // &EmptySmallSq -> &EmptySmallSqu - new Transition (2273, 2274), // &EmptyVerySmallSq -> &EmptyVerySmallSqu - new Transition (2319, 2320), // &epl -> &eplu - new Transition (2339, 2372), // &eq -> &equ - new Transition (2367, 2368), // &Eq -> &Equ - new Transition (2392, 2393), // &Equilibri -> &Equilibriu - new Transition (2565, 2566), // &FilledSmallSq -> &FilledSmallSqu - new Transition (2581, 2582), // &FilledVerySmallSq -> &FilledVerySmallSqu - new Transition (2608, 2630), // &Fo -> &Fou - new Transition (2703, 2704), // &gac -> &gacu - new Transition (2873, 2874), // &GreaterEq -> &GreaterEqu - new Transition (2883, 2884), // &GreaterF -> &GreaterFu - new Transition (2888, 2889), // &GreaterFullEq -> &GreaterFullEqu - new Transition (2912, 2913), // &GreaterSlantEq -> &GreaterSlantEqu - new Transition (2959, 2960), // >q -> >qu - new Transition (3014, 3207), // &H -> &Hu - new Transition (3078, 3080), // &hearts -> &heartsu - new Transition (3214, 3215), // &HumpDownH -> &HumpDownHu - new Transition (3220, 3221), // &HumpEq -> &HumpEqu - new Transition (3226, 3227), // &hyb -> &hybu - new Transition (3236, 3539), // &I -> &Iu - new Transition (3238, 3239), // &Iac -> &Iacu - new Transition (3243, 3544), // &i -> &iu - new Transition (3245, 3246), // &iac -> &iacu - new Transition (3497, 3498), // &iq -> &iqu - new Transition (3555, 3608), // &J -> &Ju - new Transition (3561, 3613), // &j -> &ju - new Transition (3692, 4742), // &l -> &lu - new Transition (3700, 3701), // &Lac -> &Lacu - new Transition (3706, 3707), // &lac -> &lacu - new Transition (3755, 3756), // &laq -> &laqu - new Transition (3832, 3835), // &lbrksl -> &lbrkslu - new Transition (3843, 3862), // &lc -> &lcu - new Transition (3873, 3874), // &ldq -> &ldqu - new Transition (3879, 3885), // &ldr -> &ldru - new Transition (3962, 3963), // &LeftDo -> &LeftDou - new Transition (4010, 4016), // &leftharpoon -> &leftharpoonu - new Transition (4075, 4076), // &leftrightsq -> &leftrightsqu - new Transition (4133, 4134), // &LeftTriangleEq -> &LeftTriangleEqu - new Transition (4241, 4242), // &LessEq -> &LessEqu - new Transition (4253, 4254), // &LessF -> &LessFu - new Transition (4258, 4259), // &LessFullEq -> &LessFullEqu - new Transition (4290, 4291), // &LessSlantEq -> &LessSlantEqu - new Transition (4327, 4330), // &lhar -> &lharu - new Transition (4391, 4392), // &lmo -> &lmou - new Transition (4569, 4570), // &lopl -> &loplu - new Transition (4654, 4655), // &lsaq -> &lsaqu - new Transition (4676, 4679), // &lsq -> &lsqu - new Transition (4725, 4726), // <q -> <qu - new Transition (4743, 4750), // &lur -> &luru - new Transition (4767, 4952), // &m -> &mu - new Transition (4781, 4950), // &M -> &Mu - new Transition (4789, 4801), // &mapsto -> &mapstou - new Transition (4832, 4833), // &meas -> &measu - new Transition (4845, 4846), // &Medi -> &Mediu - new Transition (4890, 4891), // &min -> &minu - new Transition (4896, 4898), // &minusd -> &minusdu - new Transition (4901, 4902), // &Min -> &Minu - new Transition (4905, 4906), // &MinusPl -> &MinusPlu - new Transition (4918, 4919), // &mnpl -> &mnplu - new Transition (4965, 6032), // &n -> &nu - new Transition (4971, 6030), // &N -> &Nu - new Transition (4973, 4974), // &Nac -> &Nacu - new Transition (4978, 4979), // &nac -> &nacu - new Transition (5001, 5002), // &nat -> &natu - new Transition (5010, 5014), // &nb -> &nbu - new Transition (5020, 5052), // &nc -> &ncu - new Transition (5094, 5095), // &NegativeMedi -> &NegativeMediu - new Transition (5135, 5136), // &neq -> &nequ - new Transition (5380, 5390), // &NotC -> &NotCu - new Transition (5384, 5385), // &NotCongr -> &NotCongru - new Transition (5397, 5398), // &NotDo -> &NotDou - new Transition (5422, 5423), // &NotEq -> &NotEqu - new Transition (5448, 5449), // &NotGreaterEq -> &NotGreaterEqu - new Transition (5453, 5454), // &NotGreaterF -> &NotGreaterFu - new Transition (5458, 5459), // &NotGreaterFullEq -> &NotGreaterFullEqu - new Transition (5482, 5483), // &NotGreaterSlantEq -> &NotGreaterSlantEqu - new Transition (5493, 5494), // &NotH -> &NotHu - new Transition (5501, 5502), // &NotHumpDownH -> &NotHumpDownHu - new Transition (5507, 5508), // &NotHumpEq -> &NotHumpEqu - new Transition (5546, 5547), // &NotLeftTriangleEq -> &NotLeftTriangleEqu - new Transition (5555, 5556), // &NotLessEq -> &NotLessEqu - new Transition (5579, 5580), // &NotLessSlantEq -> &NotLessSlantEqu - new Transition (5640, 5641), // &NotPrecedesEq -> &NotPrecedesEqu - new Transition (5651, 5652), // &NotPrecedesSlantEq -> &NotPrecedesSlantEqu - new Transition (5689, 5690), // &NotRightTriangleEq -> &NotRightTriangleEqu - new Transition (5694, 5726), // &NotS -> &NotSu - new Transition (5695, 5696), // &NotSq -> &NotSqu - new Transition (5700, 5701), // &NotSquareS -> &NotSquareSu - new Transition (5708, 5709), // &NotSquareSubsetEq -> &NotSquareSubsetEqu - new Transition (5721, 5722), // &NotSquareSupersetEq -> &NotSquareSupersetEqu - new Transition (5733, 5734), // &NotSubsetEq -> &NotSubsetEqu - new Transition (5746, 5747), // &NotSucceedsEq -> &NotSucceedsEqu - new Transition (5757, 5758), // &NotSucceedsSlantEq -> &NotSucceedsSlantEqu - new Transition (5776, 5777), // &NotSupersetEq -> &NotSupersetEqu - new Transition (5788, 5789), // &NotTildeEq -> &NotTildeEqu - new Transition (5793, 5794), // &NotTildeF -> &NotTildeFu - new Transition (5798, 5799), // &NotTildeFullEq -> &NotTildeFullEqu - new Transition (5844, 5845), // &nprc -> &nprcu - new Transition (5895, 5951), // &ns -> &nsu - new Transition (5898, 5899), // &nscc -> &nsccu - new Transition (5943, 5944), // &nsqs -> &nsqsu - new Transition (6131, 6422), // &O -> &Ou - new Transition (6133, 6134), // &Oac -> &Oacu - new Transition (6138, 6426), // &o -> &ou - new Transition (6140, 6141), // &oac -> &oacu - new Transition (6290, 6291), // &omin -> &ominu - new Transition (6309, 6310), // &OpenC -> &OpenCu - new Transition (6315, 6316), // &OpenCurlyDo -> &OpenCurlyDou - new Transition (6320, 6321), // &OpenCurlyDoubleQ -> &OpenCurlyDoubleQu - new Transition (6326, 6327), // &OpenCurlyQ -> &OpenCurlyQu - new Transition (6336, 6337), // &opl -> &oplu - new Transition (6463, 6807), // &p -> &pu - new Transition (6555, 6566), // &pl -> &plu - new Transition (6580, 6583), // &plusd -> &plusdu - new Transition (6587, 6588), // &Pl -> &Plu - new Transition (6592, 6593), // &PlusMin -> &PlusMinu - new Transition (6622, 6636), // &po -> &pou - new Transition (6642, 6790), // &pr -> &pru - new Transition (6647, 6648), // &prc -> &prcu - new Transition (6664, 6665), // &precc -> &preccu - new Transition (6680, 6681), // &PrecedesEq -> &PrecedesEqu - new Transition (6691, 6692), // &PrecedesSlantEq -> &PrecedesSlantEqu - new Transition (6749, 6750), // &Prod -> &Produ - new Transition (6765, 6766), // &profs -> &profsu - new Transition (6817, 6847), // &q -> &qu - new Transition (6876, 7601), // &r -> &ru - new Transition (6883, 6893), // &rac -> &racu - new Transition (6886, 7590), // &R -> &Ru - new Transition (6888, 6889), // &Rac -> &Racu - new Transition (6921, 6922), // &raq -> &raqu - new Transition (7016, 7019), // &rbrksl -> &rbrkslu - new Transition (7027, 7046), // &rc -> &rcu - new Transition (7063, 7064), // &rdq -> &rdqu - new Transition (7110, 7111), // &ReverseEq -> &ReverseEqu - new Transition (7117, 7118), // &ReverseEquilibri -> &ReverseEquilibriu - new Transition (7124, 7125), // &ReverseUpEq -> &ReverseUpEqu - new Transition (7131, 7132), // &ReverseUpEquilibri -> &ReverseUpEquilibriu - new Transition (7157, 7160), // &rhar -> &rharu - new Transition (7237, 7238), // &RightDo -> &RightDou - new Transition (7285, 7291), // &rightharpoon -> &rightharpoonu - new Transition (7327, 7328), // &rightsq -> &rightsqu - new Transition (7378, 7379), // &RightTriangleEq -> &RightTriangleEqu - new Transition (7454, 7455), // &rmo -> &rmou - new Transition (7485, 7501), // &Ro -> &Rou - new Transition (7491, 7492), // &ropl -> &roplu - new Transition (7544, 7545), // &rsaq -> &rsaqu - new Transition (7559, 7562), // &rsq -> &rsqu - new Transition (7602, 7603), // &rul -> &rulu - new Transition (7610, 8127), // &S -> &Su - new Transition (7612, 7613), // &Sac -> &Sacu - new Transition (7617, 8130), // &s -> &su - new Transition (7619, 7620), // &sac -> &sacu - new Transition (7625, 7626), // &sbq -> &sbqu - new Transition (7645, 7646), // &scc -> &sccu - new Transition (7732, 7733), // &setmin -> &setminu - new Transition (7869, 7870), // &simpl -> &simplu - new Transition (7903, 7904), // &smallsetmin -> &smallsetminu - new Transition (7960, 7962), // &spades -> &spadesu - new Transition (7968, 8008), // &sq -> &squ - new Transition (7969, 7975), // &sqc -> &sqcu - new Transition (7980, 8010), // &Sq -> &Squ - new Transition (7984, 7985), // &sqs -> &sqsu - new Transition (8032, 8033), // &SquareS -> &SquareSu - new Transition (8040, 8041), // &SquareSubsetEq -> &SquareSubsetEqu - new Transition (8053, 8054), // &SquareSupersetEq -> &SquareSupersetEqu - new Transition (8145, 8146), // &subm -> &submu - new Transition (8156, 8157), // &subpl -> &subplu - new Transition (8169, 8193), // &subs -> &subsu - new Transition (8179, 8180), // &SubsetEq -> &SubsetEqu - new Transition (8208, 8209), // &succc -> &succcu - new Transition (8224, 8225), // &SucceedsEq -> &SucceedsEqu - new Transition (8235, 8236), // &SucceedsSlantEq -> &SucceedsSlantEqu - new Transition (8296, 8297), // &supds -> &supdsu - new Transition (8315, 8316), // &SupersetEq -> &SupersetEqu - new Transition (8321, 8325), // &suphs -> &suphsu - new Transition (8333, 8334), // &supm -> &supmu - new Transition (8344, 8345), // &suppl -> &supplu - new Transition (8352, 8370), // &sups -> &supsu - new Transition (8401, 8411), // &Ta -> &Tau - new Transition (8405, 8413), // &ta -> &tau - new Transition (8555, 8556), // &TildeEq -> &TildeEqu - new Transition (8560, 8561), // &TildeF -> &TildeFu - new Transition (8565, 8566), // &TildeFullEq -> &TildeFullEqu - new Transition (8672, 8673), // &trimin -> &triminu - new Transition (8686, 8687), // &tripl -> &triplu - new Transition (8701, 8702), // &trpezi -> &trpeziu - new Transition (8768, 9187), // &U -> &Uu - new Transition (8770, 8771), // &Uac -> &Uacu - new Transition (8775, 9182), // &u -> &uu - new Transition (8777, 8778), // &uac -> &uacu - new Transition (8950, 8951), // &UnionPl -> &UnionPlu - new Transition (8983, 9118), // &up -> &upu - new Transition (9035, 9036), // &UpEq -> &UpEqu - new Transition (9042, 9043), // &UpEquilibri -> &UpEquilibriu - new Transition (9064, 9065), // &upl -> &uplu - new Transition (9252, 9258), // &vars -> &varsu - new Transition (9426, 9427), // &vns -> &vnsu - new Transition (9454, 9458), // &vs -> &vsu - new Transition (9548, 9645), // &x -> &xu - new Transition (9549, 9557), // &xc -> &xcu - new Transition (9614, 9615), // &xopl -> &xoplu - new Transition (9641, 9642), // &xsqc -> &xsqcu - new Transition (9647, 9648), // &xupl -> &xuplu - new Transition (9665, 9740), // &Y -> &Yu - new Transition (9667, 9668), // &Yac -> &Yacu - new Transition (9672, 9736), // &y -> &yu - new Transition (9674, 9675), // &yac -> &yacu - new Transition (9749, 9750), // &Zac -> &Zacu - new Transition (9756, 9757) // &zac -> &zacu - }; - TransitionTable_v = new Transition[75] { - new Transition (0, 9201), // & -> &v - new Transition (17, 18), // &Abre -> &Abrev - new Transition (23, 24), // &abre -> &abrev - new Transition (69, 70), // &Agra -> &Agrav - new Transition (75, 76), // &agra -> &agrav - new Transition (120, 134), // &and -> &andv - new Transition (165, 167), // &angrt -> &angrtv - new Transition (341, 342), // &Bar -> &Barv - new Transition (344, 345), // &bar -> &barv - new Transition (402, 403), // &bempty -> &bemptyv - new Transition (443, 503), // &big -> &bigv - new Transition (584, 585), // &bnequi -> &bnequiv - new Transition (613, 693), // &box -> &boxv - new Transition (726, 727), // &Bre -> &Brev - new Transition (730, 735), // &br -> &brv - new Transition (731, 732), // &bre -> &brev - new Transition (930, 931), // &cempty -> &cemptyv - new Transition (1292, 1399), // &cu -> &cuv - new Transition (1346, 1381), // &cur -> &curv - new Transition (1354, 1367), // &curly -> &curlyv - new Transition (1455, 1461), // &dash -> &dashv - new Transition (1458, 1459), // &Dash -> &Dashv - new Transition (1532, 1533), // &dempty -> &demptyv - new Transition (1589, 1590), // &DiacriticalGra -> &DiacriticalGrav - new Transition (1599, 1643), // &di -> &div - new Transition (1917, 1918), // &DownBre -> &DownBrev - new Transition (2189, 2190), // &Egra -> &Egrav - new Transition (2194, 2195), // &egra -> &egrav - new Transition (2240, 2261), // &empty -> &emptyv - new Transition (2324, 2337), // &epsi -> &epsiv - new Transition (2339, 2402), // &eq -> &eqv - new Transition (2396, 2397), // &equi -> &equiv - new Transition (2626, 2628), // &fork -> &forkv - new Transition (2701, 3002), // &g -> &gv - new Transition (2726, 2727), // &Gbre -> &Gbrev - new Transition (2732, 2733), // &gbre -> &gbrev - new Transition (2862, 2863), // &gra -> &grav - new Transition (3291, 3292), // &Igra -> &Igrav - new Transition (3297, 3298), // &igra -> &igrav - new Transition (3398, 3444), // &In -> &Inv - new Transition (3512, 3524), // &isin -> &isinv - new Transition (3520, 3522), // &isins -> &isinsv - new Transition (3628, 3630), // &kappa -> &kappav - new Transition (3692, 4755), // &l -> &lv - new Transition (3715, 3716), // &laempty -> &laemptyv - new Transition (4965, 6043), // &n -> &nv - new Transition (5088, 5089), // &Negati -> &Negativ - new Transition (5137, 5138), // &nequi -> &nequiv - new Transition (5219, 5225), // &nGt -> &nGtv - new Transition (5240, 5246), // &ni -> &niv - new Transition (5332, 5341), // &nLt -> &nLtv - new Transition (5513, 5521), // ¬in -> ¬inv - new Transition (5621, 5623), // ¬ni -> ¬niv - new Transition (5657, 5658), // &NotRe -> &NotRev - new Transition (6131, 6435), // &O -> &Ov - new Transition (6138, 6430), // &o -> &ov - new Transition (6179, 6180), // &odi -> &odiv - new Transition (6216, 6217), // &Ogra -> &Ograv - new Transition (6221, 6222), // &ogra -> &ograv - new Transition (6342, 6374), // &or -> &orv - new Transition (6528, 6530), // &phi -> &phiv - new Transition (6543, 6553), // &pi -> &piv - new Transition (6563, 6564), // &plank -> &plankv - new Transition (6905, 6906), // &raempty -> &raemptyv - new Transition (7072, 7097), // &Re -> &Rev - new Transition (7167, 7169), // &rho -> &rhov - new Transition (7841, 7845), // &sigma -> &sigmav - new Transition (8485, 8491), // &theta -> &thetav - new Transition (8807, 8808), // &Ubre -> &Ubrev - new Transition (8811, 8812), // &ubre -> &ubrev - new Transition (8862, 8863), // &Ugra -> &Ugrav - new Transition (8868, 8869), // &ugra -> &ugrav - new Transition (9303, 9471), // &V -> &Vv - new Transition (9310, 9312), // &vBar -> &vBarv - new Transition (9548, 9655) // &x -> &xv - }; - TransitionTable_w = new Transition[137] { - new Transition (0, 9490), // & -> &w - new Transition (8, 289), // &a -> &aw - new Transition (341, 349), // &Bar -> &Barw - new Transition (344, 353), // &bar -> &barw - new Transition (426, 431), // &bet -> &betw - new Transition (443, 507), // &big -> &bigw - new Transition (490, 491), // &bigtriangledo -> &bigtriangledow - new Transition (516, 517), // &bkaro -> &bkarow - new Transition (548, 549), // &blacktriangledo -> &blacktriangledow - new Transition (598, 608), // &bo -> &bow - new Transition (796, 1407), // &c -> &cw - new Transition (991, 992), // &circlearro -> &circlearrow - new Transition (1071, 1072), // &Clock -> &Clockw - new Transition (1235, 1236), // &CounterClock -> &CounterClockw - new Transition (1292, 1403), // &cu -> &cuw - new Transition (1354, 1371), // &curly -> &curlyw - new Transition (1386, 1387), // &curvearro -> &curvearrow - new Transition (1432, 2086), // &d -> &dw - new Transition (1467, 1468), // &dbkaro -> &dbkarow - new Transition (1679, 1895), // &do -> &dow - new Transition (1685, 1881), // &Do -> &Dow - new Transition (1737, 1738), // &doublebar -> &doublebarw - new Transition (1765, 1768), // &DoubleDo -> &DoubleDow - new Transition (1773, 1774), // &DoubleDownArro -> &DoubleDownArrow - new Transition (1783, 1784), // &DoubleLeftArro -> &DoubleLeftArrow - new Transition (1794, 1795), // &DoubleLeftRightArro -> &DoubleLeftRightArrow - new Transition (1811, 1812), // &DoubleLongLeftArro -> &DoubleLongLeftArrow - new Transition (1822, 1823), // &DoubleLongLeftRightArro -> &DoubleLongLeftRightArrow - new Transition (1833, 1834), // &DoubleLongRightArro -> &DoubleLongRightArrow - new Transition (1844, 1845), // &DoubleRightArro -> &DoubleRightArrow - new Transition (1856, 1857), // &DoubleUpArro -> &DoubleUpArrow - new Transition (1860, 1861), // &DoubleUpDo -> &DoubleUpDow - new Transition (1866, 1867), // &DoubleUpDownArro -> &DoubleUpDownArrow - new Transition (1886, 1887), // &DownArro -> &DownArrow - new Transition (1892, 1893), // &Downarro -> &Downarrow - new Transition (1900, 1901), // &downarro -> &downarrow - new Transition (1912, 1913), // &DownArrowUpArro -> &DownArrowUpArrow - new Transition (1922, 1923), // &downdo -> &downdow - new Transition (1928, 1929), // &downdownarro -> &downdownarrow - new Transition (2020, 2021), // &DownTeeArro -> &DownTeeArrow - new Transition (2028, 2029), // &drbkaro -> &drbkarow - new Transition (2689, 2690), // &fro -> &frow - new Transition (3050, 3056), // &harr -> &harrw - new Transition (3113, 3120), // &hks -> &hksw - new Transition (3117, 3118), // &hksearo -> &hksearow - new Transition (3123, 3124), // &hkswaro -> &hkswarow - new Transition (3145, 3146), // &hookleftarro -> &hookleftarrow - new Transition (3156, 3157), // &hookrightarro -> &hookrightarrow - new Transition (3211, 3212), // &HumpDo -> &HumpDow - new Transition (3916, 3917), // &LeftArro -> &LeftArrow - new Transition (3922, 3923), // &Leftarro -> &Leftarrow - new Transition (3930, 3931), // &leftarro -> &leftarrow - new Transition (3945, 3946), // &LeftArrowRightArro -> &LeftArrowRightArrow - new Transition (3962, 3975), // &LeftDo -> &LeftDow - new Transition (4012, 4013), // &leftharpoondo -> &leftharpoondow - new Transition (4026, 4027), // &leftleftarro -> &leftleftarrow - new Transition (4038, 4039), // &LeftRightArro -> &LeftRightArrow - new Transition (4049, 4050), // &Leftrightarro -> &Leftrightarrow - new Transition (4060, 4061), // &leftrightarro -> &leftrightarrow - new Transition (4082, 4083), // &leftrightsquigarro -> &leftrightsquigarrow - new Transition (4099, 4100), // &LeftTeeArro -> &LeftTeeArrow - new Transition (4141, 4142), // &LeftUpDo -> &LeftUpDow - new Transition (4367, 4368), // &Lleftarro -> &Lleftarrow - new Transition (4422, 4579), // &lo -> &low - new Transition (4434, 4588), // &Lo -> &Low - new Transition (4444, 4445), // &LongLeftArro -> &LongLeftArrow - new Transition (4454, 4455), // &Longleftarro -> &Longleftarrow - new Transition (4466, 4467), // &longleftarro -> &longleftarrow - new Transition (4477, 4478), // &LongLeftRightArro -> &LongLeftRightArrow - new Transition (4488, 4489), // &Longleftrightarro -> &Longleftrightarrow - new Transition (4499, 4500), // &longleftrightarro -> &longleftrightarrow - new Transition (4517, 4518), // &LongRightArro -> &LongRightArrow - new Transition (4528, 4529), // &Longrightarro -> &Longrightarrow - new Transition (4539, 4540), // &longrightarro -> &longrightarrow - new Transition (4547, 4548), // &looparro -> &looparrow - new Transition (4598, 4599), // &LowerLeftArro -> &LowerLeftArrow - new Transition (4609, 4610), // &LowerRightArro -> &LowerRightArrow - new Transition (4792, 4793), // &mapstodo -> &mapstodow - new Transition (4965, 6111), // &n -> &nw - new Transition (5077, 5078), // &nearro -> &nearrow - new Transition (5084, 5176), // &Ne -> &New - new Transition (5279, 5280), // &nLeftarro -> &nLeftarrow - new Transition (5287, 5288), // &nleftarro -> &nleftarrow - new Transition (5298, 5299), // &nLeftrightarro -> &nLeftrightarrow - new Transition (5309, 5310), // &nleftrightarro -> &nleftrightarrow - new Transition (5498, 5499), // &NotHumpDo -> &NotHumpDow - new Transition (5862, 5866), // &nrarr -> &nrarrw - new Transition (5876, 5877), // &nRightarro -> &nRightarrow - new Transition (5886, 5887), // &nrightarro -> &nrightarrow - new Transition (6123, 6124), // &nwarro -> &nwarrow - new Transition (6603, 6604), // &plust -> &plustw - new Transition (6932, 6966), // &rarr -> &rarrw - new Transition (7190, 7191), // &RightArro -> &RightArrow - new Transition (7196, 7197), // &Rightarro -> &Rightarrow - new Transition (7206, 7207), // &rightarro -> &rightarrow - new Transition (7220, 7221), // &RightArrowLeftArro -> &RightArrowLeftArrow - new Transition (7237, 7250), // &RightDo -> &RightDow - new Transition (7287, 7288), // &rightharpoondo -> &rightharpoondow - new Transition (7301, 7302), // &rightleftarro -> &rightleftarrow - new Transition (7322, 7323), // &rightrightarro -> &rightrightarrow - new Transition (7334, 7335), // &rightsquigarro -> &rightsquigarrow - new Transition (7344, 7345), // &RightTeeArro -> &RightTeeArrow - new Transition (7386, 7387), // &RightUpDo -> &RightUpDow - new Transition (7539, 7540), // &Rrightarro -> &Rrightarrow - new Transition (7617, 8375), // &s -> &sw - new Transition (7715, 7716), // &searro -> &searrow - new Transition (7724, 7725), // &ses -> &sesw - new Transition (7747, 7748), // &sfro -> &sfrow - new Transition (7777, 7778), // &ShortDo -> &ShortDow - new Transition (7783, 7784), // &ShortDownArro -> &ShortDownArrow - new Transition (7793, 7794), // &ShortLeftArro -> &ShortLeftArrow - new Transition (7820, 7821), // &ShortRightArro -> &ShortRightArrow - new Transition (7828, 7829), // &ShortUpArro -> &ShortUpArrow - new Transition (8387, 8388), // &swarro -> &swarrow - new Transition (8390, 8391), // &swn -> &swnw - new Transition (8404, 8737), // &t -> &tw - new Transition (8641, 8642), // &triangledo -> &triangledow - new Transition (8754, 8755), // &twoheadleftarro -> &twoheadleftarrow - new Transition (8765, 8766), // &twoheadrightarro -> &twoheadrightarrow - new Transition (8775, 9194), // &u -> &uw - new Transition (8974, 8975), // &UpArro -> &UpArrow - new Transition (8980, 8981), // &Uparro -> &Uparrow - new Transition (8987, 8988), // &uparro -> &uparrow - new Transition (8995, 8996), // &UpArrowDo -> &UpArrowDow - new Transition (9001, 9002), // &UpArrowDownArro -> &UpArrowDownArrow - new Transition (9005, 9006), // &UpDo -> &UpDow - new Transition (9011, 9012), // &UpDownArro -> &UpDownArrow - new Transition (9015, 9016), // &Updo -> &Updow - new Transition (9021, 9022), // &Updownarro -> &Updownarrow - new Transition (9025, 9026), // &updo -> &updow - new Transition (9031, 9032), // &updownarro -> &updownarrow - new Transition (9078, 9079), // &UpperLeftArro -> &UpperLeftArrow - new Transition (9089, 9090), // &UpperRightArro -> &UpperRightArrow - new Transition (9115, 9116), // &UpTeeArro -> &UpTeeArrow - new Transition (9123, 9124), // &upuparro -> &upuparrow - new Transition (9548, 9659), // &x -> &xw - new Transition (9754, 9848) // &z -> &zw - }; - TransitionTable_x = new Transition[24] { - new Transition (0, 9548), // & -> &x - new Transition (231, 232), // &appro -> &approx - new Transition (598, 613), // &bo -> &box - new Transition (615, 616), // &boxbo -> &boxbox - new Transition (1154, 1160), // &comple -> &complex - new Transition (1658, 1659), // &divon -> &divonx - new Transition (2108, 2466), // &E -> &Ex - new Transition (2115, 2458), // &e -> &ex - new Transition (2838, 2839), // &gnappro -> &gnapprox - new Transition (2970, 2971), // >rappro -> >rapprox - new Transition (3273, 3277), // &ie -> &iex - new Transition (4220, 4221), // &lessappro -> &lessapprox - new Transition (4407, 4408), // &lnappro -> &lnapprox - new Transition (4998, 4999), // &nappro -> &napprox - new Transition (5064, 5182), // &ne -> &nex - new Transition (5414, 5433), // &NotE -> &NotEx - new Transition (6661, 6662), // &precappro -> &precapprox - new Transition (6710, 6711), // &precnappro -> &precnapprox - new Transition (6876, 7608), // &r -> &rx - new Transition (7703, 7738), // &se -> &sex - new Transition (8205, 8206), // &succappro -> &succapprox - new Transition (8254, 8255), // &succnappro -> &succnapprox - new Transition (8500, 8501), // &thickappro -> &thickapprox - new Transition (8738, 8739) // &twi -> &twix - }; - TransitionTable_y = new Transition[122] { - new Transition (0, 9672), // & -> &y - new Transition (27, 48), // &ac -> &acy - new Transition (33, 46), // &Ac -> &Acy - new Transition (82, 83), // &alefs -> &alefsy - new Transition (218, 219), // &Appl -> &Apply - new Transition (251, 262), // &as -> &asy - new Transition (369, 377), // &bc -> &bcy - new Transition (374, 375), // &Bc -> &Bcy - new Transition (401, 402), // &bempt -> &bempty - new Transition (790, 855), // &Ca -> &Cay - new Transition (796, 1419), // &c -> &cy - new Transition (857, 858), // &Cayle -> &Cayley - new Transition (929, 930), // &cempt -> &cempty - new Transition (957, 958), // &CHc -> &CHcy - new Transition (961, 962), // &chc -> &chcy - new Transition (1097, 1098), // &CloseCurl -> &CloseCurly - new Transition (1203, 1221), // &cop -> © - new Transition (1353, 1354), // &curl -> &curly - new Transition (1422, 1423), // &cylct -> &cylcty - new Transition (1474, 1486), // &Dc -> &Dcy - new Transition (1480, 1488), // &dc -> &dcy - new Transition (1531, 1532), // &dempt -> &dempty - new Transition (1662, 1663), // &DJc -> &DJcy - new Transition (1666, 1667), // &djc -> &djcy - new Transition (2045, 2052), // &dsc -> &dscy - new Transition (2049, 2050), // &DSc -> &DScy - new Transition (2094, 2095), // &DZc -> &DZcy - new Transition (2098, 2099), // &dzc -> &dzcy - new Transition (2127, 2153), // &Ec -> &Ecy - new Transition (2133, 2155), // &ec -> &ecy - new Transition (2239, 2240), // &empt -> &empty - new Transition (2247, 2248), // &Empt -> &Empty - new Transition (2265, 2266), // &EmptyVer -> &EmptyVery - new Transition (2518, 2519), // &Fc -> &Fcy - new Transition (2521, 2522), // &fc -> &fcy - new Transition (2573, 2574), // &FilledVer -> &FilledVery - new Transition (2736, 2751), // &Gc -> &Gcy - new Transition (2746, 2753), // &gc -> &gcy - new Transition (2817, 2818), // &GJc -> &GJcy - new Transition (2821, 2822), // &gjc -> &gjcy - new Transition (3020, 3225), // &h -> &hy - new Transition (3038, 3039), // &HARDc -> &HARDcy - new Transition (3043, 3044), // &hardc -> &hardcy - new Transition (3250, 3263), // &ic -> &icy - new Transition (3252, 3261), // &Ic -> &Icy - new Transition (3270, 3271), // &IEc -> &IEcy - new Transition (3274, 3275), // &iec -> &iecy - new Transition (3348, 3349), // &Imaginar -> &Imaginary - new Transition (3464, 3465), // &IOc -> &IOcy - new Transition (3468, 3469), // &ioc -> &iocy - new Transition (3541, 3542), // &Iukc -> &Iukcy - new Transition (3546, 3547), // &iukc -> &iukcy - new Transition (3556, 3567), // &Jc -> &Jcy - new Transition (3562, 3569), // &jc -> &jcy - new Transition (3600, 3601), // &Jserc -> &Jsercy - new Transition (3605, 3606), // &jserc -> &jsercy - new Transition (3610, 3611), // &Jukc -> &Jukcy - new Transition (3615, 3616), // &jukc -> &jukcy - new Transition (3632, 3644), // &Kc -> &Kcy - new Transition (3638, 3646), // &kc -> &kcy - new Transition (3661, 3662), // &KHc -> &KHcy - new Transition (3665, 3666), // &khc -> &khcy - new Transition (3669, 3670), // &KJc -> &KJcy - new Transition (3673, 3674), // &kjc -> &kjcy - new Transition (3714, 3715), // &laempt -> &laempty - new Transition (3837, 3865), // &Lc -> &Lcy - new Transition (3843, 3867), // &lc -> &lcy - new Transition (4339, 4340), // &LJc -> &LJcy - new Transition (4343, 4344), // &ljc -> &ljcy - new Transition (4809, 4818), // &mc -> &mcy - new Transition (4815, 4816), // &Mc -> &Mcy - new Transition (5020, 5057), // &nc -> &ncy - new Transition (5024, 5055), // &Nc -> &Ncy - new Transition (5123, 5124), // &NegativeVer -> &NegativeVery - new Transition (5249, 5250), // &NJc -> &NJcy - new Transition (5253, 5254), // &njc -> &njcy - new Transition (6148, 6161), // &oc -> &ocy - new Transition (6152, 6159), // &Oc -> &Ocy - new Transition (6312, 6313), // &OpenCurl -> &OpenCurly - new Transition (6491, 6492), // &Pc -> &Pcy - new Transition (6494, 6495), // &pc -> &pcy - new Transition (6667, 6668), // &preccurl -> &preccurly - new Transition (6904, 6905), // &raempt -> &raempty - new Transition (7021, 7049), // &Rc -> &Rcy - new Transition (7027, 7051), // &rc -> &rcy - new Transition (7596, 7597), // &RuleDela -> &RuleDelay - new Transition (7629, 7691), // &Sc -> &Scy - new Transition (7631, 7693), // &sc -> &scy - new Transition (7751, 7831), // &sh -> ­ - new Transition (7759, 7760), // &SHCHc -> &SHCHcy - new Transition (7762, 7770), // &shc -> &shcy - new Transition (7764, 7765), // &shchc -> &shchcy - new Transition (7767, 7768), // &SHc -> &SHcy - new Transition (7933, 7934), // &SOFTc -> &SOFTcy - new Transition (7939, 7940), // &softc -> &softcy - new Transition (8211, 8212), // &succcurl -> &succcurly - new Transition (8419, 8441), // &Tc -> &Tcy - new Transition (8425, 8443), // &tc -> &tcy - new Transition (8487, 8488), // &thetas -> &thetasy - new Transition (8710, 8717), // &tsc -> &tscy - new Transition (8714, 8715), // &TSc -> &TScy - new Transition (8720, 8721), // &TSHc -> &TSHcy - new Transition (8724, 8725), // &tshc -> &tshcy - new Transition (8799, 8800), // &Ubrc -> &Ubrcy - new Transition (8804, 8805), // &ubrc -> &ubrcy - new Transition (8815, 8825), // &Uc -> &Ucy - new Transition (8820, 8827), // &uc -> &ucy - new Transition (9314, 9315), // &Vc -> &Vcy - new Transition (9317, 9318), // &vc -> &vcy - new Transition (9360, 9403), // &Ver -> &Very - new Transition (9674, 9683), // &yac -> &yacy - new Transition (9680, 9681), // &YAc -> &YAcy - new Transition (9685, 9695), // &Yc -> &Ycy - new Transition (9690, 9697), // &yc -> &ycy - new Transition (9709, 9710), // &YIc -> &YIcy - new Transition (9713, 9714), // &yic -> &yicy - new Transition (9733, 9734), // &YUc -> &YUcy - new Transition (9737, 9738), // &yuc -> &yucy - new Transition (9761, 9773), // &Zc -> &Zcy - new Transition (9767, 9775), // &zc -> &zcy - new Transition (9818, 9819), // &ZHc -> &ZHcy - new Transition (9822, 9823) // &zhc -> &zhcy - }; - TransitionTable_z = new Transition[10] { - new Transition (0, 9754), // & -> &z - new Transition (136, 178), // &ang -> &angz - new Transition (524, 525), // &blacklo -> &blackloz - new Transition (1432, 2097), // &d -> &dz - new Transition (3172, 3173), // &Hori -> &Horiz - new Transition (4422, 4612), // &lo -> &loz - new Transition (7617, 8395), // &s -> &sz - new Transition (8699, 8700), // &trpe -> &trpez - new Transition (9201, 9477), // &v -> &vz - new Transition (9479, 9480) // &vzig -> &vzigz - }; - - NamedEntities = new Dictionary { - [6] = "\u00C1", // Á - [7] = "\u00C1", // Á - [13] = "\u00E1", // á - [14] = "\u00E1", // á - [20] = "\u0102", // Ă - [26] = "\u0103", // ă - [28] = "\u223E", // ∾ - [30] = "\u223F", // ∿ - [32] = "\u223E\u0333", // ∾̳ - [36] = "\u00C2", //  - [37] = "\u00C2", //  - [40] = "\u00E2", // â - [41] = "\u00E2", // â - [44] = "\u00B4", // ´ - [45] = "\u00B4", // ´ - [47] = "\u0410", // А - [49] = "\u0430", // а - [53] = "\u00C6", // Æ - [54] = "\u00C6", // Æ - [58] = "\u00E6", // æ - [59] = "\u00E6", // æ - [61] = "\u2061", // ⁡ - [64] = "\uD835\uDD04", // 𝔄 - [66] = "\uD835\uDD1E", // 𝔞 - [71] = "\u00C0", // À - [72] = "\u00C0", // À - [77] = "\u00E0", // à - [78] = "\u00E0", // à - [85] = "\u2135", // ℵ - [88] = "\u2135", // ℵ - [93] = "\u0391", // Α - [97] = "\u03B1", // α - [102] = "\u0100", // Ā - [107] = "\u0101", // ā - [110] = "\u2A3F", // ⨿ - [112] = "\u0026", // & - [113] = "\u0026", // & - [114] = "\u0026", // & - [115] = "\u0026", // & - [118] = "\u2A53", // ⩓ - [121] = "\u2227", // ∧ - [125] = "\u2A55", // ⩕ - [127] = "\u2A5C", // ⩜ - [133] = "\u2A58", // ⩘ - [135] = "\u2A5A", // ⩚ - [137] = "\u2220", // ∠ - [139] = "\u29A4", // ⦤ - [142] = "\u2220", // ∠ - [146] = "\u2221", // ∡ - [149] = "\u29A8", // ⦨ - [151] = "\u29A9", // ⦩ - [153] = "\u29AA", // ⦪ - [155] = "\u29AB", // ⦫ - [157] = "\u29AC", // ⦬ - [159] = "\u29AD", // ⦭ - [161] = "\u29AE", // ⦮ - [163] = "\u29AF", // ⦯ - [166] = "\u221F", // ∟ - [169] = "\u22BE", // ⊾ - [171] = "\u299D", // ⦝ - [175] = "\u2222", // ∢ - [177] = "\u00C5", // Å - [182] = "\u237C", // ⍼ - [187] = "\u0104", // Ą - [192] = "\u0105", // ą - [195] = "\uD835\uDD38", // 𝔸 - [198] = "\uD835\uDD52", // 𝕒 - [200] = "\u2248", // ≈ - [205] = "\u2A6F", // ⩯ - [207] = "\u2A70", // ⩰ - [209] = "\u224A", // ≊ - [212] = "\u224B", // ≋ - [215] = "\u0027", // ' - [228] = "\u2061", // ⁡ - [233] = "\u2248", // ≈ - [236] = "\u224A", // ≊ - [240] = "\u00C5", // Å - [241] = "\u00C5", // Å - [245] = "\u00E5", // å - [246] = "\u00E5", // å - [250] = "\uD835\uDC9C", // 𝒜 - [254] = "\uD835\uDCB6", // 𝒶 - [259] = "\u2254", // ≔ - [261] = "\u002A", // * - [265] = "\u2248", // ≈ - [268] = "\u224D", // ≍ - [273] = "\u00C3", // à - [274] = "\u00C3", // à - [279] = "\u00E3", // ã - [280] = "\u00E3", // ã - [283] = "\u00C4", // Ä - [284] = "\u00C4", // Ä - [287] = "\u00E4", // ä - [288] = "\u00E4", // ä - [296] = "\u2233", // ∳ - [300] = "\u2A11", // ⨑ - [309] = "\u224C", // ≌ - [317] = "\u03F6", // ϶ - [323] = "\u2035", // ‵ - [327] = "\u223D", // ∽ - [330] = "\u22CD", // ⋍ - [340] = "\u2216", // ∖ - [343] = "\u2AE7", // ⫧ - [348] = "\u22BD", // ⊽ - [352] = "\u2306", // ⌆ - [356] = "\u2305", // ⌅ - [359] = "\u2305", // ⌅ - [363] = "\u23B5", // ⎵ - [368] = "\u23B6", // ⎶ - [373] = "\u224C", // ≌ - [376] = "\u0411", // Б - [378] = "\u0431", // б - [383] = "\u201E", // „ - [389] = "\u2235", // ∵ - [396] = "\u2235", // ∵ - [398] = "\u2235", // ∵ - [404] = "\u29B0", // ⦰ - [408] = "\u03F6", // ϶ - [413] = "\u212C", // ℬ - [422] = "\u212C", // ℬ - [425] = "\u0392", // Β - [428] = "\u03B2", // β - [430] = "\u2136", // ℶ - [435] = "\u226C", // ≬ - [438] = "\uD835\uDD05", // 𝔅 - [441] = "\uD835\uDD1F", // 𝔟 - [447] = "\u22C2", // ⋂ - [451] = "\u25EF", // ◯ - [454] = "\u22C3", // ⋃ - [459] = "\u2A00", // ⨀ - [464] = "\u2A01", // ⨁ - [470] = "\u2A02", // ⨂ - [476] = "\u2A06", // ⨆ - [480] = "\u2605", // ★ - [493] = "\u25BD", // ▽ - [496] = "\u25B3", // △ - [502] = "\u2A04", // ⨄ - [506] = "\u22C1", // ⋁ - [512] = "\u22C0", // ⋀ - [518] = "\u290D", // ⤍ - [530] = "\u29EB", // ⧫ - [537] = "\u25AA", // ▪ - [546] = "\u25B4", // ▴ - [551] = "\u25BE", // ▾ - [556] = "\u25C2", // ◂ - [562] = "\u25B8", // ▸ - [565] = "\u2423", // ␣ - [569] = "\u2592", // ▒ - [571] = "\u2591", // ░ - [574] = "\u2593", // ▓ - [578] = "\u2588", // █ - [581] = "\u003D\u20E5", // =⃥ - [586] = "\u2261\u20E5", // ≡⃥ - [590] = "\u2AED", // ⫭ - [593] = "\u2310", // ⌐ - [597] = "\uD835\uDD39", // 𝔹 - [601] = "\uD835\uDD53", // 𝕓 - [603] = "\u22A5", // ⊥ - [607] = "\u22A5", // ⊥ - [612] = "\u22C8", // ⋈ - [617] = "\u29C9", // ⧉ - [620] = "\u2557", // ╗ - [622] = "\u2556", // ╖ - [625] = "\u2555", // ╕ - [627] = "\u2510", // ┐ - [629] = "\u2554", // ╔ - [631] = "\u2553", // ╓ - [633] = "\u2552", // ╒ - [635] = "\u250C", // ┌ - [637] = "\u2550", // ═ - [639] = "\u2500", // ─ - [641] = "\u2566", // ╦ - [643] = "\u2564", // ╤ - [645] = "\u2565", // ╥ - [647] = "\u252C", // ┬ - [649] = "\u2569", // ╩ - [651] = "\u2567", // ╧ - [653] = "\u2568", // ╨ - [655] = "\u2534", // ┴ - [661] = "\u229F", // ⊟ - [666] = "\u229E", // ⊞ - [672] = "\u22A0", // ⊠ - [675] = "\u255D", // ╝ - [677] = "\u255C", // ╜ - [680] = "\u255B", // ╛ - [682] = "\u2518", // ┘ - [684] = "\u255A", // ╚ - [686] = "\u2559", // ╙ - [688] = "\u2558", // ╘ - [690] = "\u2514", // └ - [692] = "\u2551", // ║ - [694] = "\u2502", // │ - [696] = "\u256C", // ╬ - [698] = "\u256B", // ╫ - [700] = "\u256A", // ╪ - [702] = "\u253C", // ┼ - [704] = "\u2563", // ╣ - [706] = "\u2562", // ╢ - [708] = "\u2561", // ╡ - [710] = "\u2524", // ┤ - [712] = "\u2560", // ╠ - [714] = "\u255F", // ╟ - [716] = "\u255E", // ╞ - [718] = "\u251C", // ├ - [724] = "\u2035", // ‵ - [729] = "\u02D8", // ˘ - [734] = "\u02D8", // ˘ - [738] = "\u00A6", // ¦ - [739] = "\u00A6", // ¦ - [743] = "\u212C", // ℬ - [747] = "\uD835\uDCB7", // 𝒷 - [751] = "\u204F", // ⁏ - [754] = "\u223D", // ∽ - [756] = "\u22CD", // ⋍ - [759] = "\u005C", // \ - [761] = "\u29C5", // ⧅ - [766] = "\u27C8", // ⟈ - [770] = "\u2022", // • - [773] = "\u2022", // • - [776] = "\u224E", // ≎ - [778] = "\u2AAE", // ⪮ - [780] = "\u224F", // ≏ - [786] = "\u224E", // ≎ - [788] = "\u224F", // ≏ - [795] = "\u0106", // Ć - [802] = "\u0107", // ć - [804] = "\u22D2", // ⋒ - [806] = "\u2229", // ∩ - [810] = "\u2A44", // ⩄ - [816] = "\u2A49", // ⩉ - [820] = "\u2A4B", // ⩋ - [823] = "\u2A47", // ⩇ - [827] = "\u2A40", // ⩀ - [845] = "\u2145", // ⅅ - [847] = "\u2229\uFE00", // ∩︀ - [851] = "\u2041", // ⁁ - [854] = "\u02C7", // ˇ - [860] = "\u212D", // ℭ - [865] = "\u2A4D", // ⩍ - [871] = "\u010C", // Č - [875] = "\u010D", // č - [879] = "\u00C7", // Ç - [880] = "\u00C7", // Ç - [884] = "\u00E7", // ç - [885] = "\u00E7", // ç - [889] = "\u0108", // Ĉ - [893] = "\u0109", // ĉ - [899] = "\u2230", // ∰ - [903] = "\u2A4C", // ⩌ - [906] = "\u2A50", // ⩐ - [910] = "\u010A", // Ċ - [914] = "\u010B", // ċ - [918] = "\u00B8", // ¸ - [919] = "\u00B8", // ¸ - [926] = "\u00B8", // ¸ - [932] = "\u29B2", // ⦲ - [934] = "\u00A2", // ¢ - [935] = "\u00A2", // ¢ - [943] = "\u00B7", // · - [949] = "\u00B7", // · - [952] = "\u212D", // ℭ - [955] = "\uD835\uDD20", // 𝔠 - [959] = "\u0427", // Ч - [963] = "\u0447", // ч - [967] = "\u2713", // ✓ - [972] = "\u2713", // ✓ - [975] = "\u03A7", // Χ - [977] = "\u03C7", // χ - [980] = "\u25CB", // ○ - [982] = "\u02C6", // ˆ - [985] = "\u2257", // ≗ - [997] = "\u21BA", // ↺ - [1003] = "\u21BB", // ↻ - [1008] = "\u229B", // ⊛ - [1013] = "\u229A", // ⊚ - [1018] = "\u229D", // ⊝ - [1027] = "\u2299", // ⊙ - [1029] = "\u00AE", // ® - [1031] = "\u24C8", // Ⓢ - [1037] = "\u2296", // ⊖ - [1042] = "\u2295", // ⊕ - [1048] = "\u2297", // ⊗ - [1050] = "\u29C3", // ⧃ - [1052] = "\u2257", // ≗ - [1058] = "\u2A10", // ⨐ - [1062] = "\u2AEF", // ⫯ - [1067] = "\u29C2", // ⧂ - [1091] = "\u2232", // ∲ - [1110] = "\u201D", // ” - [1116] = "\u2019", // ’ - [1121] = "\u2663", // ♣ - [1125] = "\u2663", // ♣ - [1130] = "\u2237", // ∷ - [1135] = "\u003A", // : - [1137] = "\u2A74", // ⩴ - [1139] = "\u2254", // ≔ - [1141] = "\u2254", // ≔ - [1145] = "\u002C", // , - [1147] = "\u0040", // @ - [1149] = "\u2201", // ∁ - [1152] = "\u2218", // ∘ - [1159] = "\u2201", // ∁ - [1163] = "\u2102", // ℂ - [1166] = "\u2245", // ≅ - [1170] = "\u2A6D", // ⩭ - [1178] = "\u2261", // ≡ - [1182] = "\u222F", // ∯ - [1186] = "\u222E", // ∮ - [1199] = "\u222E", // ∮ - [1202] = "\u2102", // ℂ - [1205] = "\uD835\uDD54", // 𝕔 - [1209] = "\u2210", // ∐ - [1216] = "\u2210", // ∐ - [1219] = "\u00A9", // © - [1220] = "\u00A9", // © - [1221] = "\u00A9", // © - [1222] = "\u00A9", // © - [1225] = "\u2117", // ℗ - [1255] = "\u2233", // ∳ - [1260] = "\u21B5", // ↵ - [1265] = "\u2A2F", // ⨯ - [1269] = "\u2717", // ✗ - [1273] = "\uD835\uDC9E", // 𝒞 - [1277] = "\uD835\uDCB8", // 𝒸 - [1280] = "\u2ACF", // ⫏ - [1282] = "\u2AD1", // ⫑ - [1284] = "\u2AD0", // ⫐ - [1286] = "\u2AD2", // ⫒ - [1291] = "\u22EF", // ⋯ - [1298] = "\u2938", // ⤸ - [1300] = "\u2935", // ⤵ - [1304] = "\u22DE", // ⋞ - [1307] = "\u22DF", // ⋟ - [1312] = "\u21B6", // ↶ - [1314] = "\u293D", // ⤽ - [1317] = "\u22D3", // ⋓ - [1319] = "\u222A", // ∪ - [1325] = "\u2A48", // ⩈ - [1329] = "\u224D", // ≍ - [1333] = "\u2A46", // ⩆ - [1336] = "\u2A4A", // ⩊ - [1340] = "\u228D", // ⊍ - [1343] = "\u2A45", // ⩅ - [1345] = "\u222A\uFE00", // ∪︀ - [1350] = "\u21B7", // ↷ - [1352] = "\u293C", // ⤼ - [1361] = "\u22DE", // ⋞ - [1366] = "\u22DF", // ⋟ - [1370] = "\u22CE", // ⋎ - [1376] = "\u22CF", // ⋏ - [1379] = "\u00A4", // ¤ - [1380] = "\u00A4", // ¤ - [1392] = "\u21B6", // ↶ - [1398] = "\u21B7", // ↷ - [1402] = "\u22CE", // ⋎ - [1406] = "\u22CF", // ⋏ - [1414] = "\u2232", // ∲ - [1418] = "\u2231", // ∱ - [1424] = "\u232D", // ⌭ - [1431] = "\u2021", // ‡ - [1438] = "\u2020", // † - [1443] = "\u2138", // ℸ - [1446] = "\u21A1", // ↡ - [1450] = "\u21D3", // ⇓ - [1453] = "\u2193", // ↓ - [1456] = "\u2010", // ‐ - [1460] = "\u2AE4", // ⫤ - [1462] = "\u22A3", // ⊣ - [1469] = "\u290F", // ⤏ - [1473] = "\u02DD", // ˝ - [1479] = "\u010E", // Ď - [1485] = "\u010F", // ď - [1487] = "\u0414", // Д - [1489] = "\u0434", // д - [1491] = "\u2145", // ⅅ - [1493] = "\u2146", // ⅆ - [1499] = "\u2021", // ‡ - [1502] = "\u21CA", // ⇊ - [1509] = "\u2911", // ⤑ - [1515] = "\u2A77", // ⩷ - [1517] = "\u00B0", // ° - [1518] = "\u00B0", // ° - [1521] = "\u2207", // ∇ - [1524] = "\u0394", // Δ - [1528] = "\u03B4", // δ - [1534] = "\u29B1", // ⦱ - [1540] = "\u297F", // ⥿ - [1543] = "\uD835\uDD07", // 𝔇 - [1545] = "\uD835\uDD21", // 𝔡 - [1549] = "\u2965", // ⥥ - [1554] = "\u21C3", // ⇃ - [1556] = "\u21C2", // ⇂ - [1572] = "\u00B4", // ´ - [1576] = "\u02D9", // ˙ - [1586] = "\u02DD", // ˝ - [1592] = "\u0060", // ` - [1598] = "\u02DC", // ˜ - [1602] = "\u22C4", // ⋄ - [1607] = "\u22C4", // ⋄ - [1611] = "\u22C4", // ⋄ - [1616] = "\u2666", // ♦ - [1618] = "\u2666", // ♦ - [1620] = "\u00A8", // ¨ - [1632] = "\u2146", // ⅆ - [1638] = "\u03DD", // ϝ - [1642] = "\u22F2", // ⋲ - [1644] = "\u00F7", // ÷ - [1647] = "\u00F7", // ÷ - [1648] = "\u00F7", // ÷ - [1656] = "\u22C7", // ⋇ - [1660] = "\u22C7", // ⋇ - [1664] = "\u0402", // Ђ - [1668] = "\u0452", // ђ - [1674] = "\u231E", // ⌞ - [1678] = "\u230D", // ⌍ - [1684] = "\u0024", // $ - [1688] = "\uD835\uDD3B", // 𝔻 - [1691] = "\uD835\uDD55", // 𝕕 - [1693] = "\u00A8", // ¨ - [1695] = "\u02D9", // ˙ - [1699] = "\u20DC", // ⃜ - [1702] = "\u2250", // ≐ - [1706] = "\u2251", // ≑ - [1712] = "\u2250", // ≐ - [1718] = "\u2238", // ∸ - [1723] = "\u2214", // ∔ - [1730] = "\u22A1", // ⊡ - [1743] = "\u2306", // ⌆ - [1763] = "\u222F", // ∯ - [1767] = "\u00A8", // ¨ - [1775] = "\u21D3", // ⇓ - [1785] = "\u21D0", // ⇐ - [1796] = "\u21D4", // ⇔ - [1800] = "\u2AE4", // ⫤ - [1813] = "\u27F8", // ⟸ - [1824] = "\u27FA", // ⟺ - [1835] = "\u27F9", // ⟹ - [1846] = "\u21D2", // ⇒ - [1850] = "\u22A8", // ⊨ - [1858] = "\u21D1", // ⇑ - [1868] = "\u21D5", // ⇕ - [1880] = "\u2225", // ∥ - [1888] = "\u2193", // ↓ - [1894] = "\u21D3", // ⇓ - [1902] = "\u2193", // ↓ - [1906] = "\u2913", // ⤓ - [1914] = "\u21F5", // ⇵ - [1920] = "\u0311", // ̑ - [1931] = "\u21CA", // ⇊ - [1943] = "\u21C3", // ⇃ - [1949] = "\u21C2", // ⇂ - [1965] = "\u2950", // ⥐ - [1975] = "\u295E", // ⥞ - [1982] = "\u21BD", // ↽ - [1986] = "\u2956", // ⥖ - [2001] = "\u295F", // ⥟ - [2008] = "\u21C1", // ⇁ - [2012] = "\u2957", // ⥗ - [2016] = "\u22A4", // ⊤ - [2022] = "\u21A7", // ↧ - [2030] = "\u2910", // ⤐ - [2035] = "\u231F", // ⌟ - [2039] = "\u230C", // ⌌ - [2043] = "\uD835\uDC9F", // 𝒟 - [2047] = "\uD835\uDCB9", // 𝒹 - [2051] = "\u0405", // Ѕ - [2053] = "\u0455", // ѕ - [2056] = "\u29F6", // ⧶ - [2061] = "\u0110", // Đ - [2066] = "\u0111", // đ - [2071] = "\u22F1", // ⋱ - [2074] = "\u25BF", // ▿ - [2076] = "\u25BE", // ▾ - [2081] = "\u21F5", // ⇵ - [2085] = "\u296F", // ⥯ - [2092] = "\u29A6", // ⦦ - [2096] = "\u040F", // Џ - [2100] = "\u045F", // џ - [2107] = "\u27FF", // ⟿ - [2113] = "\u00C9", // É - [2114] = "\u00C9", // É - [2120] = "\u00E9", // é - [2121] = "\u00E9", // é - [2126] = "\u2A6E", // ⩮ - [2132] = "\u011A", // Ě - [2138] = "\u011B", // ě - [2141] = "\u2256", // ≖ - [2144] = "\u00CA", // Ê - [2145] = "\u00CA", // Ê - [2146] = "\u00EA", // ê - [2147] = "\u00EA", // ê - [2152] = "\u2255", // ≕ - [2154] = "\u042D", // Э - [2156] = "\u044D", // э - [2161] = "\u2A77", // ⩷ - [2165] = "\u0116", // Ė - [2168] = "\u2251", // ≑ - [2172] = "\u0117", // ė - [2174] = "\u2147", // ⅇ - [2179] = "\u2252", // ≒ - [2182] = "\uD835\uDD08", // 𝔈 - [2184] = "\uD835\uDD22", // 𝔢 - [2186] = "\u2A9A", // ⪚ - [2191] = "\u00C8", // È - [2192] = "\u00C8", // È - [2196] = "\u00E8", // è - [2197] = "\u00E8", // è - [2199] = "\u2A96", // ⪖ - [2203] = "\u2A98", // ⪘ - [2205] = "\u2A99", // ⪙ - [2212] = "\u2208", // ∈ - [2219] = "\u23E7", // ⏧ - [2221] = "\u2113", // ℓ - [2223] = "\u2A95", // ⪕ - [2227] = "\u2A97", // ⪗ - [2232] = "\u0112", // Ē - [2237] = "\u0113", // ē - [2241] = "\u2205", // ∅ - [2245] = "\u2205", // ∅ - [2260] = "\u25FB", // ◻ - [2262] = "\u2205", // ∅ - [2278] = "\u25AB", // ▫ - [2281] = "\u2003", //   - [2284] = "\u2004", //   - [2286] = "\u2005", //   - [2289] = "\u014A", // Ŋ - [2292] = "\u014B", // ŋ - [2295] = "\u2002", //   - [2300] = "\u0118", // Ę - [2305] = "\u0119", // ę - [2308] = "\uD835\uDD3C", // 𝔼 - [2311] = "\uD835\uDD56", // 𝕖 - [2315] = "\u22D5", // ⋕ - [2318] = "\u29E3", // ⧣ - [2322] = "\u2A71", // ⩱ - [2325] = "\u03B5", // ε - [2332] = "\u0395", // Ε - [2336] = "\u03B5", // ε - [2338] = "\u03F5", // ϵ - [2344] = "\u2256", // ≖ - [2349] = "\u2255", // ≕ - [2353] = "\u2242", // ≂ - [2361] = "\u2A96", // ⪖ - [2366] = "\u2A95", // ⪕ - [2371] = "\u2A75", // ⩵ - [2376] = "\u003D", // = - [2382] = "\u2242", // ≂ - [2386] = "\u225F", // ≟ - [2395] = "\u21CC", // ⇌ - [2398] = "\u2261", // ≡ - [2401] = "\u2A78", // ⩸ - [2408] = "\u29E5", // ⧥ - [2413] = "\u2971", // ⥱ - [2417] = "\u2253", // ≓ - [2421] = "\u2130", // ℰ - [2425] = "\u212F", // ℯ - [2429] = "\u2250", // ≐ - [2432] = "\u2A73", // ⩳ - [2435] = "\u2242", // ≂ - [2438] = "\u0397", // Η - [2441] = "\u03B7", // η - [2443] = "\u00D0", // Ð - [2444] = "\u00D0", // Ð - [2445] = "\u00F0", // ð - [2446] = "\u00F0", // ð - [2449] = "\u00CB", // Ë - [2450] = "\u00CB", // Ë - [2453] = "\u00EB", // ë - [2454] = "\u00EB", // ë - [2457] = "\u20AC", // € - [2461] = "\u0021", // ! - [2465] = "\u2203", // ∃ - [2471] = "\u2203", // ∃ - [2481] = "\u2130", // ℰ - [2492] = "\u2147", // ⅇ - [2502] = "\u2147", // ⅇ - [2516] = "\u2252", // ≒ - [2520] = "\u0424", // Ф - [2523] = "\u0444", // ф - [2529] = "\u2640", // ♀ - [2535] = "\uFB03", // ffi - [2539] = "\uFB00", // ff - [2543] = "\uFB04", // ffl - [2546] = "\uD835\uDD09", // 𝔉 - [2548] = "\uD835\uDD23", // 𝔣 - [2553] = "\uFB01", // fi - [2570] = "\u25FC", // ◼ - [2586] = "\u25AA", // ▪ - [2591] = "\u0066\u006A", // fj - [2595] = "\u266D", // ♭ - [2599] = "\uFB02", // fl - [2603] = "\u25B1", // ▱ - [2607] = "\u0192", // ƒ - [2611] = "\uD835\uDD3D", // 𝔽 - [2615] = "\uD835\uDD57", // 𝕗 - [2620] = "\u2200", // ∀ - [2625] = "\u2200", // ∀ - [2627] = "\u22D4", // ⋔ - [2629] = "\u2AD9", // ⫙ - [2638] = "\u2131", // ℱ - [2646] = "\u2A0D", // ⨍ - [2651] = "\u00BD", // ½ - [2652] = "\u00BD", // ½ - [2654] = "\u2153", // ⅓ - [2655] = "\u00BC", // ¼ - [2656] = "\u00BC", // ¼ - [2658] = "\u2155", // ⅕ - [2660] = "\u2159", // ⅙ - [2662] = "\u215B", // ⅛ - [2665] = "\u2154", // ⅔ - [2667] = "\u2156", // ⅖ - [2669] = "\u00BE", // ¾ - [2670] = "\u00BE", // ¾ - [2672] = "\u2157", // ⅗ - [2674] = "\u215C", // ⅜ - [2677] = "\u2158", // ⅘ - [2680] = "\u215A", // ⅚ - [2682] = "\u215D", // ⅝ - [2685] = "\u215E", // ⅞ - [2688] = "\u2044", // ⁄ - [2692] = "\u2322", // ⌢ - [2696] = "\u2131", // ℱ - [2700] = "\uD835\uDCBB", // 𝒻 - [2707] = "\u01F5", // ǵ - [2713] = "\u0393", // Γ - [2717] = "\u03B3", // γ - [2719] = "\u03DC", // Ϝ - [2721] = "\u03DD", // ϝ - [2723] = "\u2A86", // ⪆ - [2729] = "\u011E", // Ğ - [2735] = "\u011F", // ğ - [2741] = "\u0122", // Ģ - [2745] = "\u011C", // Ĝ - [2750] = "\u011D", // ĝ - [2752] = "\u0413", // Г - [2754] = "\u0433", // г - [2758] = "\u0120", // Ġ - [2762] = "\u0121", // ġ - [2764] = "\u2267", // ≧ - [2766] = "\u2265", // ≥ - [2768] = "\u2A8C", // ⪌ - [2770] = "\u22DB", // ⋛ - [2772] = "\u2265", // ≥ - [2774] = "\u2267", // ≧ - [2780] = "\u2A7E", // ⩾ - [2782] = "\u2A7E", // ⩾ - [2785] = "\u2AA9", // ⪩ - [2789] = "\u2A80", // ⪀ - [2791] = "\u2A82", // ⪂ - [2793] = "\u2A84", // ⪄ - [2795] = "\u22DB\uFE00", // ⋛︀ - [2798] = "\u2A94", // ⪔ - [2801] = "\uD835\uDD0A", // 𝔊 - [2804] = "\uD835\uDD24", // 𝔤 - [2806] = "\u22D9", // ⋙ - [2808] = "\u226B", // ≫ - [2810] = "\u22D9", // ⋙ - [2815] = "\u2137", // ℷ - [2819] = "\u0403", // Ѓ - [2823] = "\u0453", // ѓ - [2825] = "\u2277", // ≷ - [2827] = "\u2AA5", // ⪥ - [2829] = "\u2A92", // ⪒ - [2831] = "\u2AA4", // ⪤ - [2835] = "\u2A8A", // ⪊ - [2840] = "\u2A8A", // ⪊ - [2842] = "\u2269", // ≩ - [2844] = "\u2A88", // ⪈ - [2846] = "\u2A88", // ⪈ - [2848] = "\u2269", // ≩ - [2852] = "\u22E7", // ⋧ - [2856] = "\uD835\uDD3E", // 𝔾 - [2860] = "\uD835\uDD58", // 𝕘 - [2865] = "\u0060", // ` - [2877] = "\u2265", // ≥ - [2882] = "\u22DB", // ⋛ - [2892] = "\u2267", // ≧ - [2900] = "\u2AA2", // ⪢ - [2905] = "\u2277", // ≷ - [2916] = "\u2A7E", // ⩾ - [2922] = "\u2273", // ≳ - [2926] = "\uD835\uDCA2", // 𝒢 - [2930] = "\u210A", // ℊ - [2933] = "\u2273", // ≳ - [2935] = "\u2A8E", // ⪎ - [2937] = "\u2A90", // ⪐ - [2938] = "\u003E", // > - [2939] = "\u003E", // > - [2941] = "\u226B", // ≫ - [2942] = "\u003E", // > - [2943] = "\u003E", // > - [2946] = "\u2AA7", // ⪧ - [2949] = "\u2A7A", // ⩺ - [2953] = "\u22D7", // ⋗ - [2958] = "\u2995", // ⦕ - [2964] = "\u2A7C", // ⩼ - [2972] = "\u2A86", // ⪆ - [2975] = "\u2978", // ⥸ - [2979] = "\u22D7", // ⋗ - [2986] = "\u22DB", // ⋛ - [2992] = "\u2A8C", // ⪌ - [2997] = "\u2277", // ≷ - [3001] = "\u2273", // ≳ - [3010] = "\u2269\uFE00", // ≩︀ - [3013] = "\u2269\uFE00", // ≩︀ - [3019] = "\u02C7", // ˇ - [3026] = "\u200A", //   - [3029] = "\u00BD", // ½ - [3034] = "\u210B", // ℋ - [3040] = "\u042A", // Ъ - [3045] = "\u044A", // ъ - [3049] = "\u21D4", // ⇔ - [3051] = "\u2194", // ↔ - [3055] = "\u2948", // ⥈ - [3057] = "\u21AD", // ↭ - [3059] = "\u005E", // ^ - [3063] = "\u210F", // ℏ - [3068] = "\u0124", // Ĥ - [3073] = "\u0125", // ĥ - [3079] = "\u2665", // ♥ - [3083] = "\u2665", // ♥ - [3088] = "\u2026", // … - [3093] = "\u22B9", // ⊹ - [3096] = "\u210C", // ℌ - [3099] = "\uD835\uDD25", // 𝔥 - [3111] = "\u210B", // ℋ - [3119] = "\u2925", // ⤥ - [3125] = "\u2926", // ⤦ - [3130] = "\u21FF", // ⇿ - [3135] = "\u223B", // ∻ - [3147] = "\u21A9", // ↩ - [3158] = "\u21AA", // ↪ - [3162] = "\u210D", // ℍ - [3165] = "\uD835\uDD59", // 𝕙 - [3170] = "\u2015", // ― - [3183] = "\u2500", // ─ - [3187] = "\u210B", // ℋ - [3191] = "\uD835\uDCBD", // 𝒽 - [3196] = "\u210F", // ℏ - [3201] = "\u0126", // Ħ - [3206] = "\u0127", // ħ - [3218] = "\u224E", // ≎ - [3224] = "\u224F", // ≏ - [3230] = "\u2043", // ⁃ - [3235] = "\u2010", // ‐ - [3241] = "\u00CD", // Í - [3242] = "\u00CD", // Í - [3248] = "\u00ED", // í - [3249] = "\u00ED", // í - [3251] = "\u2063", // ⁣ - [3255] = "\u00CE", // Î - [3256] = "\u00CE", // Î - [3259] = "\u00EE", // î - [3260] = "\u00EE", // î - [3262] = "\u0418", // И - [3264] = "\u0438", // и - [3268] = "\u0130", // İ - [3272] = "\u0415", // Е - [3276] = "\u0435", // е - [3279] = "\u00A1", // ¡ - [3280] = "\u00A1", // ¡ - [3283] = "\u21D4", // ⇔ - [3286] = "\u2111", // ℑ - [3288] = "\uD835\uDD26", // 𝔦 - [3293] = "\u00CC", // Ì - [3294] = "\u00CC", // Ì - [3299] = "\u00EC", // ì - [3300] = "\u00EC", // ì - [3302] = "\u2148", // ⅈ - [3307] = "\u2A0C", // ⨌ - [3310] = "\u222D", // ∭ - [3315] = "\u29DC", // ⧜ - [3319] = "\u2129", // ℩ - [3324] = "\u0132", // IJ - [3329] = "\u0133", // ij - [3331] = "\u2111", // ℑ - [3335] = "\u012A", // Ī - [3340] = "\u012B", // ī - [3343] = "\u2111", // ℑ - [3351] = "\u2148", // ⅈ - [3356] = "\u2110", // ℐ - [3361] = "\u2111", // ℑ - [3364] = "\u0131", // ı - [3367] = "\u22B7", // ⊷ - [3371] = "\u01B5", // Ƶ - [3377] = "\u21D2", // ⇒ - [3379] = "\u2208", // ∈ - [3384] = "\u2105", // ℅ - [3388] = "\u221E", // ∞ - [3392] = "\u29DD", // ⧝ - [3397] = "\u0131", // ı - [3400] = "\u222C", // ∬ - [3402] = "\u222B", // ∫ - [3406] = "\u22BA", // ⊺ - [3412] = "\u2124", // ℤ - [3418] = "\u222B", // ∫ - [3423] = "\u22BA", // ⊺ - [3432] = "\u22C2", // ⋂ - [3438] = "\u2A17", // ⨗ - [3443] = "\u2A3C", // ⨼ - [3456] = "\u2063", // ⁣ - [3462] = "\u2062", // ⁢ - [3466] = "\u0401", // Ё - [3470] = "\u0451", // ё - [3475] = "\u012E", // Į - [3479] = "\u012F", // į - [3482] = "\uD835\uDD40", // 𝕀 - [3485] = "\uD835\uDD5A", // 𝕚 - [3488] = "\u0399", // Ι - [3491] = "\u03B9", // ι - [3496] = "\u2A3C", // ⨼ - [3501] = "\u00BF", // ¿ - [3502] = "\u00BF", // ¿ - [3506] = "\u2110", // ℐ - [3510] = "\uD835\uDCBE", // 𝒾 - [3513] = "\u2208", // ∈ - [3517] = "\u22F5", // ⋵ - [3519] = "\u22F9", // ⋹ - [3521] = "\u22F4", // ⋴ - [3523] = "\u22F3", // ⋳ - [3525] = "\u2208", // ∈ - [3527] = "\u2062", // ⁢ - [3533] = "\u0128", // Ĩ - [3538] = "\u0129", // ĩ - [3543] = "\u0406", // І - [3548] = "\u0456", // і - [3550] = "\u00CF", // Ï - [3551] = "\u00CF", // Ï - [3553] = "\u00EF", // ï - [3554] = "\u00EF", // ï - [3560] = "\u0134", // Ĵ - [3566] = "\u0135", // ĵ - [3568] = "\u0419", // Й - [3570] = "\u0439", // й - [3573] = "\uD835\uDD0D", // 𝔍 - [3576] = "\uD835\uDD27", // 𝔧 - [3581] = "\u0237", // ȷ - [3585] = "\uD835\uDD41", // 𝕁 - [3589] = "\uD835\uDD5B", // 𝕛 - [3593] = "\uD835\uDCA5", // 𝒥 - [3597] = "\uD835\uDCBF", // 𝒿 - [3602] = "\u0408", // Ј - [3607] = "\u0458", // ј - [3612] = "\u0404", // Є - [3617] = "\u0454", // є - [3623] = "\u039A", // Κ - [3629] = "\u03BA", // κ - [3631] = "\u03F0", // ϰ - [3637] = "\u0136", // Ķ - [3643] = "\u0137", // ķ - [3645] = "\u041A", // К - [3647] = "\u043A", // к - [3650] = "\uD835\uDD0E", // 𝔎 - [3653] = "\uD835\uDD28", // 𝔨 - [3659] = "\u0138", // ĸ - [3663] = "\u0425", // Х - [3667] = "\u0445", // х - [3671] = "\u040C", // Ќ - [3675] = "\u045C", // ќ - [3679] = "\uD835\uDD42", // 𝕂 - [3683] = "\uD835\uDD5C", // 𝕜 - [3687] = "\uD835\uDCA6", // 𝒦 - [3691] = "\uD835\uDCC0", // 𝓀 - [3697] = "\u21DA", // ⇚ - [3704] = "\u0139", // Ĺ - [3710] = "\u013A", // ĺ - [3717] = "\u29B4", // ⦴ - [3722] = "\u2112", // ℒ - [3727] = "\u039B", // Λ - [3732] = "\u03BB", // λ - [3735] = "\u27EA", // ⟪ - [3738] = "\u27E8", // ⟨ - [3740] = "\u2991", // ⦑ - [3743] = "\u27E8", // ⟨ - [3745] = "\u2A85", // ⪅ - [3754] = "\u2112", // ℒ - [3757] = "\u00AB", // « - [3758] = "\u00AB", // « - [3761] = "\u219E", // ↞ - [3764] = "\u21D0", // ⇐ - [3767] = "\u2190", // ← - [3769] = "\u21E4", // ⇤ - [3772] = "\u291F", // ⤟ - [3775] = "\u291D", // ⤝ - [3778] = "\u21A9", // ↩ - [3781] = "\u21AB", // ↫ - [3784] = "\u2939", // ⤹ - [3788] = "\u2973", // ⥳ - [3791] = "\u21A2", // ↢ - [3793] = "\u2AAB", // ⪫ - [3798] = "\u291B", // ⤛ - [3802] = "\u2919", // ⤙ - [3804] = "\u2AAD", // ⪭ - [3806] = "\u2AAD\uFE00", // ⪭︀ - [3811] = "\u290E", // ⤎ - [3816] = "\u290C", // ⤌ - [3820] = "\u2772", // ❲ - [3825] = "\u007B", // { - [3827] = "\u005B", // [ - [3830] = "\u298B", // ⦋ - [3834] = "\u298F", // ⦏ - [3836] = "\u298D", // ⦍ - [3842] = "\u013D", // Ľ - [3848] = "\u013E", // ľ - [3853] = "\u013B", // Ļ - [3858] = "\u013C", // ļ - [3861] = "\u2308", // ⌈ - [3864] = "\u007B", // { - [3866] = "\u041B", // Л - [3868] = "\u043B", // л - [3872] = "\u2936", // ⤶ - [3876] = "\u201C", // “ - [3878] = "\u201E", // „ - [3884] = "\u2967", // ⥧ - [3890] = "\u294B", // ⥋ - [3893] = "\u21B2", // ↲ - [3895] = "\u2266", // ≦ - [3897] = "\u2264", // ≤ - [3913] = "\u27E8", // ⟨ - [3918] = "\u2190", // ← - [3924] = "\u21D0", // ⇐ - [3932] = "\u2190", // ← - [3936] = "\u21E4", // ⇤ - [3947] = "\u21C6", // ⇆ - [3952] = "\u21A2", // ↢ - [3960] = "\u2308", // ⌈ - [3974] = "\u27E6", // ⟦ - [3986] = "\u2961", // ⥡ - [3993] = "\u21C3", // ⇃ - [3997] = "\u2959", // ⥙ - [4003] = "\u230A", // ⌊ - [4015] = "\u21BD", // ↽ - [4018] = "\u21BC", // ↼ - [4029] = "\u21C7", // ⇇ - [4040] = "\u2194", // ↔ - [4051] = "\u21D4", // ⇔ - [4062] = "\u2194", // ↔ - [4064] = "\u21C6", // ⇆ - [4073] = "\u21CB", // ⇋ - [4084] = "\u21AD", // ↭ - [4091] = "\u294E", // ⥎ - [4095] = "\u22A3", // ⊣ - [4101] = "\u21A4", // ↤ - [4108] = "\u295A", // ⥚ - [4119] = "\u22CB", // ⋋ - [4127] = "\u22B2", // ⊲ - [4131] = "\u29CF", // ⧏ - [4137] = "\u22B4", // ⊴ - [4150] = "\u2951", // ⥑ - [4160] = "\u2960", // ⥠ - [4167] = "\u21BF", // ↿ - [4171] = "\u2958", // ⥘ - [4178] = "\u21BC", // ↼ - [4182] = "\u2952", // ⥒ - [4184] = "\u2A8B", // ⪋ - [4186] = "\u22DA", // ⋚ - [4188] = "\u2264", // ≤ - [4190] = "\u2266", // ≦ - [4196] = "\u2A7D", // ⩽ - [4198] = "\u2A7D", // ⩽ - [4201] = "\u2AA8", // ⪨ - [4205] = "\u2A7F", // ⩿ - [4207] = "\u2A81", // ⪁ - [4209] = "\u2A83", // ⪃ - [4211] = "\u22DA\uFE00", // ⋚︀ - [4214] = "\u2A93", // ⪓ - [4222] = "\u2A85", // ⪅ - [4226] = "\u22D6", // ⋖ - [4232] = "\u22DA", // ⋚ - [4237] = "\u2A8B", // ⪋ - [4252] = "\u22DA", // ⋚ - [4262] = "\u2266", // ≦ - [4270] = "\u2276", // ≶ - [4274] = "\u2276", // ≶ - [4279] = "\u2AA1", // ⪡ - [4283] = "\u2272", // ≲ - [4294] = "\u2A7D", // ⩽ - [4300] = "\u2272", // ≲ - [4306] = "\u297C", // ⥼ - [4311] = "\u230A", // ⌊ - [4314] = "\uD835\uDD0F", // 𝔏 - [4316] = "\uD835\uDD29", // 𝔩 - [4318] = "\u2276", // ≶ - [4320] = "\u2A91", // ⪑ - [4324] = "\u2962", // ⥢ - [4329] = "\u21BD", // ↽ - [4331] = "\u21BC", // ↼ - [4333] = "\u296A", // ⥪ - [4337] = "\u2584", // ▄ - [4341] = "\u0409", // Љ - [4345] = "\u0459", // љ - [4347] = "\u22D8", // ⋘ - [4349] = "\u226A", // ≪ - [4353] = "\u21C7", // ⇇ - [4360] = "\u231E", // ⌞ - [4369] = "\u21DA", // ⇚ - [4374] = "\u296B", // ⥫ - [4378] = "\u25FA", // ◺ - [4384] = "\u013F", // Ŀ - [4390] = "\u0140", // ŀ - [4395] = "\u23B0", // ⎰ - [4400] = "\u23B0", // ⎰ - [4404] = "\u2A89", // ⪉ - [4409] = "\u2A89", // ⪉ - [4411] = "\u2268", // ≨ - [4413] = "\u2A87", // ⪇ - [4415] = "\u2A87", // ⪇ - [4417] = "\u2268", // ≨ - [4421] = "\u22E6", // ⋦ - [4426] = "\u27EC", // ⟬ - [4429] = "\u21FD", // ⇽ - [4433] = "\u27E6", // ⟦ - [4446] = "\u27F5", // ⟵ - [4456] = "\u27F8", // ⟸ - [4468] = "\u27F5", // ⟵ - [4479] = "\u27F7", // ⟷ - [4490] = "\u27FA", // ⟺ - [4501] = "\u27F7", // ⟷ - [4508] = "\u27FC", // ⟼ - [4519] = "\u27F6", // ⟶ - [4530] = "\u27F9", // ⟹ - [4541] = "\u27F6", // ⟶ - [4553] = "\u21AB", // ↫ - [4559] = "\u21AC", // ↬ - [4563] = "\u2985", // ⦅ - [4566] = "\uD835\uDD43", // 𝕃 - [4568] = "\uD835\uDD5D", // 𝕝 - [4572] = "\u2A2D", // ⨭ - [4578] = "\u2A34", // ⨴ - [4583] = "\u2217", // ∗ - [4587] = "\u005F", // _ - [4600] = "\u2199", // ↙ - [4611] = "\u2198", // ↘ - [4613] = "\u25CA", // ◊ - [4618] = "\u25CA", // ◊ - [4620] = "\u29EB", // ⧫ - [4624] = "\u0028", // ( - [4627] = "\u2993", // ⦓ - [4632] = "\u21C6", // ⇆ - [4639] = "\u231F", // ⌟ - [4643] = "\u21CB", // ⇋ - [4645] = "\u296D", // ⥭ - [4647] = "\u200E", // ‎ - [4651] = "\u22BF", // ⊿ - [4657] = "\u2039", // ‹ - [4661] = "\u2112", // ℒ - [4664] = "\uD835\uDCC1", // 𝓁 - [4666] = "\u21B0", // ↰ - [4668] = "\u21B0", // ↰ - [4671] = "\u2272", // ≲ - [4673] = "\u2A8D", // ⪍ - [4675] = "\u2A8F", // ⪏ - [4678] = "\u005B", // [ - [4681] = "\u2018", // ‘ - [4683] = "\u201A", // ‚ - [4688] = "\u0141", // Ł - [4693] = "\u0142", // ł - [4694] = "\u003C", // < - [4695] = "\u003C", // < - [4697] = "\u226A", // ≪ - [4698] = "\u003C", // < - [4699] = "\u003C", // < - [4702] = "\u2AA6", // ⪦ - [4705] = "\u2A79", // ⩹ - [4709] = "\u22D6", // ⋖ - [4714] = "\u22CB", // ⋋ - [4719] = "\u22C9", // ⋉ - [4724] = "\u2976", // ⥶ - [4730] = "\u2A7B", // ⩻ - [4733] = "\u25C3", // ◃ - [4735] = "\u22B4", // ⊴ - [4737] = "\u25C2", // ◂ - [4741] = "\u2996", // ⦖ - [4749] = "\u294A", // ⥊ - [4754] = "\u2966", // ⥦ - [4763] = "\u2268\uFE00", // ≨︀ - [4766] = "\u2268\uFE00", // ≨︀ - [4770] = "\u00AF", // ¯ - [4771] = "\u00AF", // ¯ - [4774] = "\u2642", // ♂ - [4776] = "\u2720", // ✠ - [4780] = "\u2720", // ✠ - [4784] = "\u2905", // ⤅ - [4786] = "\u21A6", // ↦ - [4790] = "\u21A6", // ↦ - [4795] = "\u21A7", // ↧ - [4800] = "\u21A4", // ↤ - [4803] = "\u21A5", // ↥ - [4808] = "\u25AE", // ▮ - [4814] = "\u2A29", // ⨩ - [4817] = "\u041C", // М - [4819] = "\u043C", // м - [4824] = "\u2014", // — - [4829] = "\u223A", // ∺ - [4842] = "\u2221", // ∡ - [4853] = "\u205F", //   - [4861] = "\u2133", // ℳ - [4864] = "\uD835\uDD10", // 𝔐 - [4867] = "\uD835\uDD2A", // 𝔪 - [4870] = "\u2127", // ℧ - [4874] = "\u00B5", // µ - [4875] = "\u00B5", // µ - [4877] = "\u2223", // ∣ - [4881] = "\u002A", // * - [4885] = "\u2AF0", // ⫰ - [4888] = "\u00B7", // · - [4889] = "\u00B7", // · - [4893] = "\u2212", // − - [4895] = "\u229F", // ⊟ - [4897] = "\u2238", // ∸ - [4899] = "\u2A2A", // ⨪ - [4908] = "\u2213", // ∓ - [4912] = "\u2ADB", // ⫛ - [4915] = "\u2026", // … - [4921] = "\u2213", // ∓ - [4927] = "\u22A7", // ⊧ - [4931] = "\uD835\uDD44", // 𝕄 - [4934] = "\uD835\uDD5E", // 𝕞 - [4936] = "\u2213", // ∓ - [4940] = "\u2133", // ℳ - [4944] = "\uD835\uDCC2", // 𝓂 - [4949] = "\u223E", // ∾ - [4951] = "\u039C", // Μ - [4953] = "\u03BC", // μ - [4960] = "\u22B8", // ⊸ - [4964] = "\u22B8", // ⊸ - [4970] = "\u2207", // ∇ - [4977] = "\u0143", // Ń - [4982] = "\u0144", // ń - [4985] = "\u2220\u20D2", // ∠⃒ - [4987] = "\u2249", // ≉ - [4989] = "\u2A70\u0338", // ⩰̸ - [4992] = "\u224B\u0338", // ≋̸ - [4995] = "\u0149", // ʼn - [5000] = "\u2249", // ≉ - [5004] = "\u266E", // ♮ - [5007] = "\u266E", // ♮ - [5009] = "\u2115", // ℕ - [5012] = "\u00A0", //   - [5013] = "\u00A0", //   - [5017] = "\u224E\u0338", // ≎̸ - [5019] = "\u224F\u0338", // ≏̸ - [5023] = "\u2A43", // ⩃ - [5029] = "\u0147", // Ň - [5033] = "\u0148", // ň - [5038] = "\u0145", // Ņ - [5043] = "\u0146", // ņ - [5047] = "\u2247", // ≇ - [5051] = "\u2A6D\u0338", // ⩭̸ - [5054] = "\u2A42", // ⩂ - [5056] = "\u041D", // Н - [5058] = "\u043D", // н - [5063] = "\u2013", // – - [5065] = "\u2260", // ≠ - [5070] = "\u2924", // ⤤ - [5074] = "\u21D7", // ⇗ - [5076] = "\u2197", // ↗ - [5079] = "\u2197", // ↗ - [5083] = "\u2250\u0338", // ≐̸ - [5102] = "\u200B", // ​ - [5113] = "\u200B", // ​ - [5120] = "\u200B", // ​ - [5134] = "\u200B", // ​ - [5139] = "\u2262", // ≢ - [5144] = "\u2928", // ⤨ - [5147] = "\u2242\u0338", // ≂̸ - [5166] = "\u226B", // ≫ - [5175] = "\u226A", // ≪ - [5181] = "\u000A", // - [5186] = "\u2204", // ∄ - [5188] = "\u2204", // ∄ - [5191] = "\uD835\uDD11", // 𝔑 - [5194] = "\uD835\uDD2B", // 𝔫 - [5197] = "\u2267\u0338", // ≧̸ - [5199] = "\u2271", // ≱ - [5201] = "\u2271", // ≱ - [5203] = "\u2267\u0338", // ≧̸ - [5209] = "\u2A7E\u0338", // ⩾̸ - [5211] = "\u2A7E\u0338", // ⩾̸ - [5214] = "\u22D9\u0338", // ⋙̸ - [5218] = "\u2275", // ≵ - [5220] = "\u226B\u20D2", // ≫⃒ - [5222] = "\u226F", // ≯ - [5224] = "\u226F", // ≯ - [5226] = "\u226B\u0338", // ≫̸ - [5231] = "\u21CE", // ⇎ - [5235] = "\u21AE", // ↮ - [5239] = "\u2AF2", // ⫲ - [5241] = "\u220B", // ∋ - [5243] = "\u22FC", // ⋼ - [5245] = "\u22FA", // ⋺ - [5247] = "\u220B", // ∋ - [5251] = "\u040A", // Њ - [5255] = "\u045A", // њ - [5260] = "\u21CD", // ⇍ - [5264] = "\u219A", // ↚ - [5267] = "\u2025", // ‥ - [5269] = "\u2266\u0338", // ≦̸ - [5271] = "\u2270", // ≰ - [5281] = "\u21CD", // ⇍ - [5289] = "\u219A", // ↚ - [5300] = "\u21CE", // ⇎ - [5311] = "\u21AE", // ↮ - [5313] = "\u2270", // ≰ - [5315] = "\u2266\u0338", // ≦̸ - [5321] = "\u2A7D\u0338", // ⩽̸ - [5323] = "\u2A7D\u0338", // ⩽̸ - [5325] = "\u226E", // ≮ - [5327] = "\u22D8\u0338", // ⋘̸ - [5331] = "\u2274", // ≴ - [5333] = "\u226A\u20D2", // ≪⃒ - [5335] = "\u226E", // ≮ - [5338] = "\u22EA", // ⋪ - [5340] = "\u22EC", // ⋬ - [5342] = "\u226A\u0338", // ≪̸ - [5346] = "\u2224", // ∤ - [5353] = "\u2060", // ⁠ - [5368] = "\u00A0", //   - [5371] = "\u2115", // ℕ - [5375] = "\uD835\uDD5F", // 𝕟 - [5377] = "\u2AEC", // ⫬ - [5378] = "\u00AC", // ¬ - [5379] = "\u00AC", // ¬ - [5389] = "\u2262", // ≢ - [5395] = "\u226D", // ≭ - [5413] = "\u2226", // ∦ - [5421] = "\u2209", // ∉ - [5426] = "\u2260", // ≠ - [5432] = "\u2242\u0338", // ≂̸ - [5438] = "\u2204", // ∄ - [5446] = "\u226F", // ≯ - [5452] = "\u2271", // ≱ - [5462] = "\u2267\u0338", // ≧̸ - [5470] = "\u226B\u0338", // ≫̸ - [5475] = "\u2279", // ≹ - [5486] = "\u2A7E\u0338", // ⩾̸ - [5492] = "\u2275", // ≵ - [5505] = "\u224E\u0338", // ≎̸ - [5511] = "\u224F\u0338", // ≏̸ - [5514] = "\u2209", // ∉ - [5518] = "\u22F5\u0338", // ⋵̸ - [5520] = "\u22F9\u0338", // ⋹̸ - [5523] = "\u2209", // ∉ - [5525] = "\u22F7", // ⋷ - [5527] = "\u22F6", // ⋶ - [5540] = "\u22EA", // ⋪ - [5544] = "\u29CF\u0338", // ⧏̸ - [5550] = "\u22EC", // ⋬ - [5553] = "\u226E", // ≮ - [5559] = "\u2270", // ≰ - [5567] = "\u2278", // ≸ - [5572] = "\u226A\u0338", // ≪̸ - [5583] = "\u2A7D\u0338", // ⩽̸ - [5589] = "\u2274", // ≴ - [5610] = "\u2AA2\u0338", // ⪢̸ - [5619] = "\u2AA1\u0338", // ⪡̸ - [5622] = "\u220C", // ∌ - [5625] = "\u220C", // ∌ - [5627] = "\u22FE", // ⋾ - [5629] = "\u22FD", // ⋽ - [5638] = "\u2280", // ⊀ - [5644] = "\u2AAF\u0338", // ⪯̸ - [5655] = "\u22E0", // ⋠ - [5670] = "\u220C", // ∌ - [5683] = "\u22EB", // ⋫ - [5687] = "\u29D0\u0338", // ⧐̸ - [5693] = "\u22ED", // ⋭ - [5706] = "\u228F\u0338", // ⊏̸ - [5712] = "\u22E2", // ⋢ - [5719] = "\u2290\u0338", // ⊐̸ - [5725] = "\u22E3", // ⋣ - [5731] = "\u2282\u20D2", // ⊂⃒ - [5737] = "\u2288", // ⊈ - [5744] = "\u2281", // ⊁ - [5750] = "\u2AB0\u0338", // ⪰̸ - [5761] = "\u22E1", // ⋡ - [5767] = "\u227F\u0338", // ≿̸ - [5774] = "\u2283\u20D2", // ⊃⃒ - [5780] = "\u2289", // ⊉ - [5786] = "\u2241", // ≁ - [5792] = "\u2244", // ≄ - [5802] = "\u2247", // ≇ - [5808] = "\u2249", // ≉ - [5820] = "\u2224", // ∤ - [5824] = "\u2226", // ∦ - [5830] = "\u2226", // ∦ - [5833] = "\u2AFD\u20E5", // ⫽⃥ - [5835] = "\u2202\u0338", // ∂̸ - [5841] = "\u2A14", // ⨔ - [5843] = "\u2280", // ⊀ - [5847] = "\u22E0", // ⋠ - [5849] = "\u2AAF\u0338", // ⪯̸ - [5851] = "\u2280", // ⊀ - [5854] = "\u2AAF\u0338", // ⪯̸ - [5859] = "\u21CF", // ⇏ - [5863] = "\u219B", // ↛ - [5865] = "\u2933\u0338", // ⤳̸ - [5867] = "\u219D\u0338", // ↝̸ - [5878] = "\u21CF", // ⇏ - [5888] = "\u219B", // ↛ - [5892] = "\u22EB", // ⋫ - [5894] = "\u22ED", // ⋭ - [5897] = "\u2281", // ⊁ - [5901] = "\u22E1", // ⋡ - [5903] = "\u2AB0\u0338", // ⪰̸ - [5907] = "\uD835\uDCA9", // 𝒩 - [5909] = "\uD835\uDCC3", // 𝓃 - [5917] = "\u2224", // ∤ - [5926] = "\u2226", // ∦ - [5929] = "\u2241", // ≁ - [5931] = "\u2244", // ≄ - [5933] = "\u2244", // ≄ - [5937] = "\u2224", // ∤ - [5941] = "\u2226", // ∦ - [5947] = "\u22E2", // ⋢ - [5950] = "\u22E3", // ⋣ - [5953] = "\u2284", // ⊄ - [5955] = "\u2AC5\u0338", // ⫅̸ - [5957] = "\u2288", // ⊈ - [5961] = "\u2282\u20D2", // ⊂⃒ - [5964] = "\u2288", // ⊈ - [5966] = "\u2AC5\u0338", // ⫅̸ - [5969] = "\u2281", // ⊁ - [5972] = "\u2AB0\u0338", // ⪰̸ - [5974] = "\u2285", // ⊅ - [5976] = "\u2AC6\u0338", // ⫆̸ - [5978] = "\u2289", // ⊉ - [5982] = "\u2283\u20D2", // ⊃⃒ - [5985] = "\u2289", // ⊉ - [5987] = "\u2AC6\u0338", // ⫆̸ - [5991] = "\u2279", // ≹ - [5996] = "\u00D1", // Ñ - [5997] = "\u00D1", // Ñ - [6001] = "\u00F1", // ñ - [6002] = "\u00F1", // ñ - [6005] = "\u2278", // ≸ - [6017] = "\u22EA", // ⋪ - [6020] = "\u22EC", // ⋬ - [6026] = "\u22EB", // ⋫ - [6029] = "\u22ED", // ⋭ - [6031] = "\u039D", // Ν - [6033] = "\u03BD", // ν - [6035] = "\u0023", // # - [6039] = "\u2116", // № - [6042] = "\u2007", //   - [6046] = "\u224D\u20D2", // ≍⃒ - [6052] = "\u22AF", // ⊯ - [6057] = "\u22AE", // ⊮ - [6062] = "\u22AD", // ⊭ - [6067] = "\u22AC", // ⊬ - [6070] = "\u2265\u20D2", // ≥⃒ - [6072] = "\u003E\u20D2", // >⃒ - [6077] = "\u2904", // ⤄ - [6083] = "\u29DE", // ⧞ - [6088] = "\u2902", // ⤂ - [6090] = "\u2264\u20D2", // ≤⃒ - [6092] = "\u003C\u20D2", // <⃒ - [6096] = "\u22B4\u20D2", // ⊴⃒ - [6101] = "\u2903", // ⤃ - [6106] = "\u22B5\u20D2", // ⊵⃒ - [6110] = "\u223C\u20D2", // ∼⃒ - [6116] = "\u2923", // ⤣ - [6120] = "\u21D6", // ⇖ - [6122] = "\u2196", // ↖ - [6125] = "\u2196", // ↖ - [6130] = "\u2927", // ⤧ - [6136] = "\u00D3", // Ó - [6137] = "\u00D3", // Ó - [6143] = "\u00F3", // ó - [6144] = "\u00F3", // ó - [6147] = "\u229B", // ⊛ - [6151] = "\u229A", // ⊚ - [6155] = "\u00D4", // Ô - [6156] = "\u00D4", // Ô - [6157] = "\u00F4", // ô - [6158] = "\u00F4", // ô - [6160] = "\u041E", // О - [6162] = "\u043E", // о - [6167] = "\u229D", // ⊝ - [6173] = "\u0150", // Ő - [6178] = "\u0151", // ő - [6181] = "\u2A38", // ⨸ - [6184] = "\u2299", // ⊙ - [6189] = "\u29BC", // ⦼ - [6194] = "\u0152", // Œ - [6199] = "\u0153", // œ - [6204] = "\u29BF", // ⦿ - [6207] = "\uD835\uDD12", // 𝔒 - [6209] = "\uD835\uDD2C", // 𝔬 - [6213] = "\u02DB", // ˛ - [6218] = "\u00D2", // Ò - [6219] = "\u00D2", // Ò - [6223] = "\u00F2", // ò - [6224] = "\u00F2", // ò - [6226] = "\u29C1", // ⧁ - [6231] = "\u29B5", // ⦵ - [6233] = "\u03A9", // Ω - [6237] = "\u222E", // ∮ - [6242] = "\u21BA", // ↺ - [6246] = "\u29BE", // ⦾ - [6251] = "\u29BB", // ⦻ - [6255] = "\u203E", // ‾ - [6257] = "\u29C0", // ⧀ - [6262] = "\u014C", // Ō - [6267] = "\u014D", // ō - [6271] = "\u03A9", // Ω - [6275] = "\u03C9", // ω - [6281] = "\u039F", // Ο - [6287] = "\u03BF", // ο - [6289] = "\u29B6", // ⦶ - [6293] = "\u2296", // ⊖ - [6297] = "\uD835\uDD46", // 𝕆 - [6301] = "\uD835\uDD60", // 𝕠 - [6305] = "\u29B7", // ⦷ - [6325] = "\u201C", // “ - [6331] = "\u2018", // ‘ - [6335] = "\u29B9", // ⦹ - [6339] = "\u2295", // ⊕ - [6341] = "\u2A54", // ⩔ - [6343] = "\u2228", // ∨ - [6347] = "\u21BB", // ↻ - [6349] = "\u2A5D", // ⩝ - [6352] = "\u2134", // ℴ - [6355] = "\u2134", // ℴ - [6356] = "\u00AA", // ª - [6357] = "\u00AA", // ª - [6358] = "\u00BA", // º - [6359] = "\u00BA", // º - [6364] = "\u22B6", // ⊶ - [6367] = "\u2A56", // ⩖ - [6373] = "\u2A57", // ⩗ - [6375] = "\u2A5B", // ⩛ - [6377] = "\u24C8", // Ⓢ - [6381] = "\uD835\uDCAA", // 𝒪 - [6385] = "\u2134", // ℴ - [6389] = "\u00D8", // Ø - [6390] = "\u00D8", // Ø - [6394] = "\u00F8", // ø - [6395] = "\u00F8", // ø - [6398] = "\u2298", // ⊘ - [6403] = "\u00D5", // Õ - [6404] = "\u00D5", // Õ - [6409] = "\u00F5", // õ - [6410] = "\u00F5", // õ - [6414] = "\u2A37", // ⨷ - [6418] = "\u2297", // ⊗ - [6421] = "\u2A36", // ⨶ - [6424] = "\u00D6", // Ö - [6425] = "\u00D6", // Ö - [6428] = "\u00F6", // ö - [6429] = "\u00F6", // ö - [6434] = "\u233D", // ⌽ - [6441] = "\u203E", // ‾ - [6446] = "\u23DE", // ⏞ - [6450] = "\u23B4", // ⎴ - [6462] = "\u23DC", // ⏜ - [6466] = "\u2225", // ∥ - [6467] = "\u00B6", // ¶ - [6468] = "\u00B6", // ¶ - [6473] = "\u2225", // ∥ - [6477] = "\u2AF3", // ⫳ - [6479] = "\u2AFD", // ⫽ - [6481] = "\u2202", // ∂ - [6490] = "\u2202", // ∂ - [6493] = "\u041F", // П - [6496] = "\u043F", // п - [6502] = "\u0025", // % - [6506] = "\u002E", // . - [6510] = "\u2030", // ‰ - [6512] = "\u22A5", // ⊥ - [6517] = "\u2031", // ‱ - [6520] = "\uD835\uDD13", // 𝔓 - [6523] = "\uD835\uDD2D", // 𝔭 - [6526] = "\u03A6", // Φ - [6529] = "\u03C6", // φ - [6531] = "\u03D5", // ϕ - [6536] = "\u2133", // ℳ - [6540] = "\u260E", // ☎ - [6542] = "\u03A0", // Π - [6544] = "\u03C0", // π - [6552] = "\u22D4", // ⋔ - [6554] = "\u03D6", // ϖ - [6560] = "\u210F", // ℏ - [6562] = "\u210E", // ℎ - [6565] = "\u210F", // ℏ - [6568] = "\u002B", // + - [6573] = "\u2A23", // ⨣ - [6575] = "\u229E", // ⊞ - [6579] = "\u2A22", // ⨢ - [6582] = "\u2214", // ∔ - [6584] = "\u2A25", // ⨥ - [6586] = "\u2A72", // ⩲ - [6595] = "\u00B1", // ± - [6597] = "\u00B1", // ± - [6598] = "\u00B1", // ± - [6602] = "\u2A26", // ⨦ - [6606] = "\u2A27", // ⨧ - [6608] = "\u00B1", // ± - [6621] = "\u210C", // ℌ - [6629] = "\u2A15", // ⨕ - [6632] = "\u2119", // ℙ - [6635] = "\uD835\uDD61", // 𝕡 - [6638] = "\u00A3", // £ - [6639] = "\u00A3", // £ - [6641] = "\u2ABB", // ⪻ - [6643] = "\u227A", // ≺ - [6646] = "\u2AB7", // ⪷ - [6650] = "\u227C", // ≼ - [6652] = "\u2AB3", // ⪳ - [6654] = "\u2AAF", // ⪯ - [6656] = "\u227A", // ≺ - [6663] = "\u2AB7", // ⪷ - [6671] = "\u227C", // ≼ - [6678] = "\u227A", // ≺ - [6684] = "\u2AAF", // ⪯ - [6695] = "\u227C", // ≼ - [6701] = "\u227E", // ≾ - [6704] = "\u2AAF", // ⪯ - [6712] = "\u2AB9", // ⪹ - [6716] = "\u2AB5", // ⪵ - [6720] = "\u22E8", // ⋨ - [6724] = "\u227E", // ≾ - [6728] = "\u2033", // ″ - [6732] = "\u2032", // ′ - [6734] = "\u2119", // ℙ - [6738] = "\u2AB9", // ⪹ - [6740] = "\u2AB5", // ⪵ - [6744] = "\u22E8", // ⋨ - [6747] = "\u220F", // ∏ - [6753] = "\u220F", // ∏ - [6759] = "\u232E", // ⌮ - [6764] = "\u2312", // ⌒ - [6769] = "\u2313", // ⌓ - [6771] = "\u221D", // ∝ - [6779] = "\u2237", // ∷ - [6782] = "\u221D", // ∝ - [6785] = "\u221D", // ∝ - [6789] = "\u227E", // ≾ - [6794] = "\u22B0", // ⊰ - [6798] = "\uD835\uDCAB", // 𝒫 - [6802] = "\uD835\uDCC5", // 𝓅 - [6804] = "\u03A8", // Ψ - [6806] = "\u03C8", // ψ - [6812] = "\u2008", //   - [6816] = "\uD835\uDD14", // 𝔔 - [6820] = "\uD835\uDD2E", // 𝔮 - [6824] = "\u2A0C", // ⨌ - [6828] = "\u211A", // ℚ - [6832] = "\uD835\uDD62", // 𝕢 - [6838] = "\u2057", // ⁗ - [6842] = "\uD835\uDCAC", // 𝒬 - [6846] = "\uD835\uDCC6", // 𝓆 - [6857] = "\u210D", // ℍ - [6861] = "\u2A16", // ⨖ - [6865] = "\u003F", // ? - [6868] = "\u225F", // ≟ - [6871] = "\u0022", // " - [6872] = "\u0022", // " - [6874] = "\u0022", // " - [6875] = "\u0022", // " - [6881] = "\u21DB", // ⇛ - [6885] = "\u223D\u0331", // ∽̱ - [6892] = "\u0154", // Ŕ - [6896] = "\u0155", // ŕ - [6900] = "\u221A", // √ - [6907] = "\u29B3", // ⦳ - [6910] = "\u27EB", // ⟫ - [6913] = "\u27E9", // ⟩ - [6915] = "\u2992", // ⦒ - [6917] = "\u29A5", // ⦥ - [6920] = "\u27E9", // ⟩ - [6923] = "\u00BB", // » - [6924] = "\u00BB", // » - [6927] = "\u21A0", // ↠ - [6930] = "\u21D2", // ⇒ - [6933] = "\u2192", // → - [6936] = "\u2975", // ⥵ - [6938] = "\u21E5", // ⇥ - [6941] = "\u2920", // ⤠ - [6943] = "\u2933", // ⤳ - [6946] = "\u291E", // ⤞ - [6949] = "\u21AA", // ↪ - [6952] = "\u21AC", // ↬ - [6955] = "\u2945", // ⥅ - [6959] = "\u2974", // ⥴ - [6962] = "\u2916", // ⤖ - [6965] = "\u21A3", // ↣ - [6967] = "\u219D", // ↝ - [6972] = "\u291C", // ⤜ - [6977] = "\u291A", // ⤚ - [6980] = "\u2236", // ∶ - [6985] = "\u211A", // ℚ - [6990] = "\u2910", // ⤐ - [6995] = "\u290F", // ⤏ - [7000] = "\u290D", // ⤍ - [7004] = "\u2773", // ❳ - [7009] = "\u007D", // } - [7011] = "\u005D", // ] - [7014] = "\u298C", // ⦌ - [7018] = "\u298E", // ⦎ - [7020] = "\u2990", // ⦐ - [7026] = "\u0158", // Ř - [7032] = "\u0159", // ř - [7037] = "\u0156", // Ŗ - [7042] = "\u0157", // ŗ - [7045] = "\u2309", // ⌉ - [7048] = "\u007D", // } - [7050] = "\u0420", // Р - [7052] = "\u0440", // р - [7056] = "\u2937", // ⤷ - [7062] = "\u2969", // ⥩ - [7066] = "\u201D", // ” - [7068] = "\u201D", // ” - [7071] = "\u21B3", // ↳ - [7073] = "\u211C", // ℜ - [7077] = "\u211C", // ℜ - [7081] = "\u211B", // ℛ - [7086] = "\u211C", // ℜ - [7088] = "\u211D", // ℝ - [7091] = "\u25AD", // ▭ - [7093] = "\u00AE", // ® - [7094] = "\u00AE", // ® - [7095] = "\u00AE", // ® - [7096] = "\u00AE", // ® - [7109] = "\u220B", // ∋ - [7120] = "\u21CB", // ⇋ - [7134] = "\u296F", // ⥯ - [7140] = "\u297D", // ⥽ - [7145] = "\u230B", // ⌋ - [7148] = "\u211C", // ℜ - [7150] = "\uD835\uDD2F", // 𝔯 - [7154] = "\u2964", // ⥤ - [7159] = "\u21C1", // ⇁ - [7161] = "\u21C0", // ⇀ - [7163] = "\u296C", // ⥬ - [7166] = "\u03A1", // Ρ - [7168] = "\u03C1", // ρ - [7170] = "\u03F1", // ϱ - [7187] = "\u27E9", // ⟩ - [7192] = "\u2192", // → - [7198] = "\u21D2", // ⇒ - [7208] = "\u2192", // → - [7212] = "\u21E5", // ⇥ - [7222] = "\u21C4", // ⇄ - [7227] = "\u21A3", // ↣ - [7235] = "\u2309", // ⌉ - [7249] = "\u27E7", // ⟧ - [7261] = "\u295D", // ⥝ - [7268] = "\u21C2", // ⇂ - [7272] = "\u2955", // ⥕ - [7278] = "\u230B", // ⌋ - [7290] = "\u21C1", // ⇁ - [7293] = "\u21C0", // ⇀ - [7304] = "\u21C4", // ⇄ - [7313] = "\u21CC", // ⇌ - [7325] = "\u21C9", // ⇉ - [7336] = "\u219D", // ↝ - [7340] = "\u22A2", // ⊢ - [7346] = "\u21A6", // ↦ - [7353] = "\u295B", // ⥛ - [7364] = "\u22CC", // ⋌ - [7372] = "\u22B3", // ⊳ - [7376] = "\u29D0", // ⧐ - [7382] = "\u22B5", // ⊵ - [7395] = "\u294F", // ⥏ - [7405] = "\u295C", // ⥜ - [7412] = "\u21BE", // ↾ - [7416] = "\u2954", // ⥔ - [7423] = "\u21C0", // ⇀ - [7427] = "\u2953", // ⥓ - [7430] = "\u02DA", // ˚ - [7441] = "\u2253", // ≓ - [7446] = "\u21C4", // ⇄ - [7450] = "\u21CC", // ⇌ - [7452] = "\u200F", // ‏ - [7458] = "\u23B1", // ⎱ - [7463] = "\u23B1", // ⎱ - [7468] = "\u2AEE", // ⫮ - [7473] = "\u27ED", // ⟭ - [7476] = "\u21FE", // ⇾ - [7480] = "\u27E7", // ⟧ - [7484] = "\u2986", // ⦆ - [7488] = "\u211D", // ℝ - [7490] = "\uD835\uDD63", // 𝕣 - [7494] = "\u2A2E", // ⨮ - [7500] = "\u2A35", // ⨵ - [7511] = "\u2970", // ⥰ - [7515] = "\u0029", // ) - [7518] = "\u2994", // ⦔ - [7525] = "\u2A12", // ⨒ - [7530] = "\u21C9", // ⇉ - [7541] = "\u21DB", // ⇛ - [7547] = "\u203A", // › - [7551] = "\u211B", // ℛ - [7554] = "\uD835\uDCC7", // 𝓇 - [7556] = "\u21B1", // ↱ - [7558] = "\u21B1", // ↱ - [7561] = "\u005D", // ] - [7564] = "\u2019", // ’ - [7566] = "\u2019", // ’ - [7572] = "\u22CC", // ⋌ - [7577] = "\u22CA", // ⋊ - [7580] = "\u25B9", // ▹ - [7582] = "\u22B5", // ⊵ - [7584] = "\u25B8", // ▸ - [7589] = "\u29CE", // ⧎ - [7600] = "\u29F4", // ⧴ - [7607] = "\u2968", // ⥨ - [7609] = "\u211E", // ℞ - [7616] = "\u015A", // Ś - [7623] = "\u015B", // ś - [7628] = "\u201A", // ‚ - [7630] = "\u2ABC", // ⪼ - [7632] = "\u227B", // ≻ - [7635] = "\u2AB8", // ⪸ - [7640] = "\u0160", // Š - [7644] = "\u0161", // š - [7648] = "\u227D", // ≽ - [7650] = "\u2AB4", // ⪴ - [7652] = "\u2AB0", // ⪰ - [7657] = "\u015E", // Ş - [7661] = "\u015F", // ş - [7665] = "\u015C", // Ŝ - [7669] = "\u015D", // ŝ - [7673] = "\u2ABA", // ⪺ - [7675] = "\u2AB6", // ⪶ - [7679] = "\u22E9", // ⋩ - [7686] = "\u2A13", // ⨓ - [7690] = "\u227F", // ≿ - [7692] = "\u0421", // С - [7694] = "\u0441", // с - [7698] = "\u22C5", // ⋅ - [7700] = "\u22A1", // ⊡ - [7702] = "\u2A66", // ⩦ - [7708] = "\u2925", // ⤥ - [7712] = "\u21D8", // ⇘ - [7714] = "\u2198", // ↘ - [7717] = "\u2198", // ↘ - [7719] = "\u00A7", // § - [7720] = "\u00A7", // § - [7723] = "\u003B", // ; - [7728] = "\u2929", // ⤩ - [7735] = "\u2216", // ∖ - [7737] = "\u2216", // ∖ - [7740] = "\u2736", // ✶ - [7743] = "\uD835\uDD16", // 𝔖 - [7746] = "\uD835\uDD30", // 𝔰 - [7750] = "\u2322", // ⌢ - [7755] = "\u266F", // ♯ - [7761] = "\u0429", // Щ - [7766] = "\u0449", // щ - [7769] = "\u0428", // Ш - [7771] = "\u0448", // ш - [7785] = "\u2193", // ↓ - [7795] = "\u2190", // ← - [7802] = "\u2223", // ∣ - [7811] = "\u2225", // ∥ - [7822] = "\u2192", // → - [7830] = "\u2191", // ↑ - [7831] = "\u00AD", // ­ - [7832] = "\u00AD", // ­ - [7837] = "\u03A3", // Σ - [7842] = "\u03C3", // σ - [7844] = "\u03C2", // ς - [7846] = "\u03C2", // ς - [7848] = "\u223C", // ∼ - [7852] = "\u2A6A", // ⩪ - [7854] = "\u2243", // ≃ - [7856] = "\u2243", // ≃ - [7858] = "\u2A9E", // ⪞ - [7860] = "\u2AA0", // ⪠ - [7862] = "\u2A9D", // ⪝ - [7864] = "\u2A9F", // ⪟ - [7867] = "\u2246", // ≆ - [7872] = "\u2A24", // ⨤ - [7877] = "\u2972", // ⥲ - [7882] = "\u2190", // ← - [7893] = "\u2218", // ∘ - [7906] = "\u2216", // ∖ - [7910] = "\u2A33", // ⨳ - [7917] = "\u29E4", // ⧤ - [7920] = "\u2223", // ∣ - [7923] = "\u2323", // ⌣ - [7925] = "\u2AAA", // ⪪ - [7927] = "\u2AAC", // ⪬ - [7929] = "\u2AAC\uFE00", // ⪬︀ - [7935] = "\u042C", // Ь - [7941] = "\u044C", // ь - [7943] = "\u002F", // / - [7945] = "\u29C4", // ⧄ - [7948] = "\u233F", // ⌿ - [7952] = "\uD835\uDD4A", // 𝕊 - [7955] = "\uD835\uDD64", // 𝕤 - [7961] = "\u2660", // ♠ - [7965] = "\u2660", // ♠ - [7967] = "\u2225", // ∥ - [7972] = "\u2293", // ⊓ - [7974] = "\u2293\uFE00", // ⊓︀ - [7977] = "\u2294", // ⊔ - [7979] = "\u2294\uFE00", // ⊔︀ - [7983] = "\u221A", // √ - [7987] = "\u228F", // ⊏ - [7989] = "\u2291", // ⊑ - [7993] = "\u228F", // ⊏ - [7996] = "\u2291", // ⊑ - [7998] = "\u2290", // ⊐ - [8000] = "\u2292", // ⊒ - [8004] = "\u2290", // ⊐ - [8007] = "\u2292", // ⊒ - [8009] = "\u25A1", // □ - [8014] = "\u25A1", // □ - [8018] = "\u25A1", // □ - [8031] = "\u2293", // ⊓ - [8038] = "\u228F", // ⊏ - [8044] = "\u2291", // ⊑ - [8051] = "\u2290", // ⊐ - [8057] = "\u2292", // ⊒ - [8063] = "\u2294", // ⊔ - [8065] = "\u25AA", // ▪ - [8067] = "\u25AA", // ▪ - [8072] = "\u2192", // → - [8076] = "\uD835\uDCAE", // 𝒮 - [8080] = "\uD835\uDCC8", // 𝓈 - [8085] = "\u2216", // ∖ - [8090] = "\u2323", // ⌣ - [8095] = "\u22C6", // ⋆ - [8099] = "\u22C6", // ⋆ - [8103] = "\u2606", // ☆ - [8105] = "\u2605", // ★ - [8119] = "\u03F5", // ϵ - [8123] = "\u03D5", // ϕ - [8126] = "\u00AF", // ¯ - [8129] = "\u22D0", // ⋐ - [8132] = "\u2282", // ⊂ - [8136] = "\u2ABD", // ⪽ - [8138] = "\u2AC5", // ⫅ - [8140] = "\u2286", // ⊆ - [8144] = "\u2AC3", // ⫃ - [8149] = "\u2AC1", // ⫁ - [8152] = "\u2ACB", // ⫋ - [8154] = "\u228A", // ⊊ - [8159] = "\u2ABF", // ⪿ - [8164] = "\u2979", // ⥹ - [8168] = "\u22D0", // ⋐ - [8172] = "\u2282", // ⊂ - [8175] = "\u2286", // ⊆ - [8177] = "\u2AC5", // ⫅ - [8183] = "\u2286", // ⊆ - [8187] = "\u228A", // ⊊ - [8189] = "\u2ACB", // ⫋ - [8192] = "\u2AC7", // ⫇ - [8195] = "\u2AD5", // ⫕ - [8197] = "\u2AD3", // ⫓ - [8200] = "\u227B", // ≻ - [8207] = "\u2AB8", // ⪸ - [8215] = "\u227D", // ≽ - [8222] = "\u227B", // ≻ - [8228] = "\u2AB0", // ⪰ - [8239] = "\u227D", // ≽ - [8245] = "\u227F", // ≿ - [8248] = "\u2AB0", // ⪰ - [8256] = "\u2ABA", // ⪺ - [8260] = "\u2AB6", // ⪶ - [8264] = "\u22E9", // ⋩ - [8268] = "\u227F", // ≿ - [8274] = "\u220B", // ∋ - [8276] = "\u2211", // ∑ - [8278] = "\u2211", // ∑ - [8281] = "\u266A", // ♪ - [8283] = "\u22D1", // ⋑ - [8285] = "\u2283", // ⊃ - [8286] = "\u00B9", // ¹ - [8287] = "\u00B9", // ¹ - [8288] = "\u00B2", // ² - [8289] = "\u00B2", // ² - [8290] = "\u00B3", // ³ - [8291] = "\u00B3", // ³ - [8295] = "\u2ABE", // ⪾ - [8299] = "\u2AD8", // ⫘ - [8301] = "\u2AC6", // ⫆ - [8303] = "\u2287", // ⊇ - [8307] = "\u2AC4", // ⫄ - [8313] = "\u2283", // ⊃ - [8319] = "\u2287", // ⊇ - [8324] = "\u27C9", // ⟉ - [8327] = "\u2AD7", // ⫗ - [8332] = "\u297B", // ⥻ - [8337] = "\u2AC2", // ⫂ - [8340] = "\u2ACC", // ⫌ - [8342] = "\u228B", // ⊋ - [8347] = "\u2AC0", // ⫀ - [8351] = "\u22D1", // ⋑ - [8355] = "\u2283", // ⊃ - [8358] = "\u2287", // ⊇ - [8360] = "\u2AC6", // ⫆ - [8364] = "\u228B", // ⊋ - [8366] = "\u2ACC", // ⫌ - [8369] = "\u2AC8", // ⫈ - [8372] = "\u2AD4", // ⫔ - [8374] = "\u2AD6", // ⫖ - [8380] = "\u2926", // ⤦ - [8384] = "\u21D9", // ⇙ - [8386] = "\u2199", // ↙ - [8389] = "\u2199", // ↙ - [8394] = "\u292A", // ⤪ - [8398] = "\u00DF", // ß - [8399] = "\u00DF", // ß - [8403] = "\u0009", // - [8410] = "\u2316", // ⌖ - [8412] = "\u03A4", // Τ - [8414] = "\u03C4", // τ - [8418] = "\u23B4", // ⎴ - [8424] = "\u0164", // Ť - [8430] = "\u0165", // ť - [8435] = "\u0162", // Ţ - [8440] = "\u0163", // ţ - [8442] = "\u0422", // Т - [8444] = "\u0442", // т - [8448] = "\u20DB", // ⃛ - [8454] = "\u2315", // ⌕ - [8457] = "\uD835\uDD17", // 𝔗 - [8460] = "\uD835\uDD31", // 𝔱 - [8466] = "\u2234", // ∴ - [8475] = "\u2234", // ∴ - [8480] = "\u2234", // ∴ - [8483] = "\u0398", // Θ - [8486] = "\u03B8", // θ - [8490] = "\u03D1", // ϑ - [8492] = "\u03D1", // ϑ - [8502] = "\u2248", // ≈ - [8506] = "\u223C", // ∼ - [8515] = "\u205F\u200A", //    - [8519] = "\u2009", //   - [8526] = "\u2009", //   - [8530] = "\u2248", // ≈ - [8534] = "\u223C", // ∼ - [8538] = "\u00DE", // Þ - [8539] = "\u00DE", // Þ - [8542] = "\u00FE", // þ - [8543] = "\u00FE", // þ - [8548] = "\u223C", // ∼ - [8553] = "\u02DC", // ˜ - [8559] = "\u2243", // ≃ - [8569] = "\u2245", // ≅ - [8575] = "\u2248", // ≈ - [8578] = "\u00D7", // × - [8579] = "\u00D7", // × - [8581] = "\u22A0", // ⊠ - [8584] = "\u2A31", // ⨱ - [8586] = "\u2A30", // ⨰ - [8589] = "\u222D", // ∭ - [8593] = "\u2928", // ⤨ - [8595] = "\u22A4", // ⊤ - [8599] = "\u2336", // ⌶ - [8603] = "\u2AF1", // ⫱ - [8607] = "\uD835\uDD4B", // 𝕋 - [8609] = "\uD835\uDD65", // 𝕥 - [8613] = "\u2ADA", // ⫚ - [8616] = "\u2929", // ⤩ - [8622] = "\u2034", // ‴ - [8627] = "\u2122", // ™ - [8632] = "\u2122", // ™ - [8639] = "\u25B5", // ▵ - [8644] = "\u25BF", // ▿ - [8649] = "\u25C3", // ◃ - [8652] = "\u22B4", // ⊴ - [8654] = "\u225C", // ≜ - [8660] = "\u25B9", // ▹ - [8663] = "\u22B5", // ⊵ - [8667] = "\u25EC", // ◬ - [8669] = "\u225C", // ≜ - [8675] = "\u2A3A", // ⨺ - [8684] = "\u20DB", // ⃛ - [8689] = "\u2A39", // ⨹ - [8692] = "\u29CD", // ⧍ - [8697] = "\u2A3B", // ⨻ - [8704] = "\u23E2", // ⏢ - [8708] = "\uD835\uDCAF", // 𝒯 - [8712] = "\uD835\uDCC9", // 𝓉 - [8716] = "\u0426", // Ц - [8718] = "\u0446", // ц - [8722] = "\u040B", // Ћ - [8726] = "\u045B", // ћ - [8731] = "\u0166", // Ŧ - [8736] = "\u0167", // ŧ - [8741] = "\u226C", // ≬ - [8756] = "\u219E", // ↞ - [8767] = "\u21A0", // ↠ - [8773] = "\u00DA", // Ú - [8774] = "\u00DA", // Ú - [8780] = "\u00FA", // ú - [8781] = "\u00FA", // ú - [8784] = "\u219F", // ↟ - [8788] = "\u21D1", // ⇑ - [8791] = "\u2191", // ↑ - [8796] = "\u2949", // ⥉ - [8801] = "\u040E", // Ў - [8806] = "\u045E", // ў - [8810] = "\u016C", // Ŭ - [8814] = "\u016D", // ŭ - [8818] = "\u00DB", // Û - [8819] = "\u00DB", // Û - [8823] = "\u00FB", // û - [8824] = "\u00FB", // û - [8826] = "\u0423", // У - [8828] = "\u0443", // у - [8833] = "\u21C5", // ⇅ - [8839] = "\u0170", // Ű - [8844] = "\u0171", // ű - [8848] = "\u296E", // ⥮ - [8854] = "\u297E", // ⥾ - [8857] = "\uD835\uDD18", // 𝔘 - [8859] = "\uD835\uDD32", // 𝔲 - [8864] = "\u00D9", // Ù - [8865] = "\u00D9", // Ù - [8870] = "\u00F9", // ù - [8871] = "\u00F9", // ù - [8875] = "\u2963", // ⥣ - [8880] = "\u21BF", // ↿ - [8882] = "\u21BE", // ↾ - [8886] = "\u2580", // ▀ - [8892] = "\u231C", // ⌜ - [8895] = "\u231C", // ⌜ - [8899] = "\u230F", // ⌏ - [8903] = "\u25F8", // ◸ - [8908] = "\u016A", // Ū - [8913] = "\u016B", // ū - [8914] = "\u00A8", // ¨ - [8915] = "\u00A8", // ¨ - [8923] = "\u005F", // _ - [8928] = "\u23DF", // ⏟ - [8932] = "\u23B5", // ⎵ - [8944] = "\u23DD", // ⏝ - [8948] = "\u22C3", // ⋃ - [8953] = "\u228E", // ⊎ - [8958] = "\u0172", // Ų - [8963] = "\u0173", // ų - [8966] = "\uD835\uDD4C", // 𝕌 - [8969] = "\uD835\uDD66", // 𝕦 - [8976] = "\u2191", // ↑ - [8982] = "\u21D1", // ⇑ - [8989] = "\u2191", // ↑ - [8993] = "\u2912", // ⤒ - [9003] = "\u21C5", // ⇅ - [9013] = "\u2195", // ↕ - [9023] = "\u21D5", // ⇕ - [9033] = "\u2195", // ↕ - [9045] = "\u296E", // ⥮ - [9057] = "\u21BF", // ↿ - [9063] = "\u21BE", // ↾ - [9067] = "\u228E", // ⊎ - [9080] = "\u2196", // ↖ - [9091] = "\u2197", // ↗ - [9094] = "\u03D2", // ϒ - [9097] = "\u03C5", // υ - [9099] = "\u03D2", // ϒ - [9103] = "\u03A5", // Υ - [9107] = "\u03C5", // υ - [9111] = "\u22A5", // ⊥ - [9117] = "\u21A5", // ↥ - [9126] = "\u21C8", // ⇈ - [9132] = "\u231D", // ⌝ - [9135] = "\u231D", // ⌝ - [9139] = "\u230E", // ⌎ - [9144] = "\u016E", // Ů - [9148] = "\u016F", // ů - [9152] = "\u25F9", // ◹ - [9156] = "\uD835\uDCB0", // 𝒰 - [9160] = "\uD835\uDCCA", // 𝓊 - [9165] = "\u22F0", // ⋰ - [9171] = "\u0168", // Ũ - [9176] = "\u0169", // ũ - [9179] = "\u25B5", // ▵ - [9181] = "\u25B4", // ▴ - [9186] = "\u21C8", // ⇈ - [9189] = "\u00DC", // Ü - [9190] = "\u00DC", // Ü - [9192] = "\u00FC", // ü - [9193] = "\u00FC", // ü - [9200] = "\u29A7", // ⦧ - [9207] = "\u299C", // ⦜ - [9216] = "\u03F5", // ϵ - [9222] = "\u03F0", // ϰ - [9230] = "\u2205", // ∅ - [9234] = "\u03D5", // ϕ - [9236] = "\u03D6", // ϖ - [9242] = "\u221D", // ∝ - [9246] = "\u21D5", // ⇕ - [9248] = "\u2195", // ↕ - [9251] = "\u03F1", // ϱ - [9257] = "\u03C2", // ς - [9266] = "\u228A\uFE00", // ⊊︀ - [9268] = "\u2ACB\uFE00", // ⫋︀ - [9276] = "\u228B\uFE00", // ⊋︀ - [9278] = "\u2ACC\uFE00", // ⫌︀ - [9284] = "\u03D1", // ϑ - [9296] = "\u22B2", // ⊲ - [9302] = "\u22B3", // ⊳ - [9307] = "\u2AEB", // ⫫ - [9311] = "\u2AE8", // ⫨ - [9313] = "\u2AE9", // ⫩ - [9316] = "\u0412", // В - [9319] = "\u0432", // в - [9324] = "\u22AB", // ⊫ - [9329] = "\u22A9", // ⊩ - [9334] = "\u22A8", // ⊨ - [9339] = "\u22A2", // ⊢ - [9341] = "\u2AE6", // ⫦ - [9344] = "\u22C1", // ⋁ - [9347] = "\u2228", // ∨ - [9351] = "\u22BB", // ⊻ - [9354] = "\u225A", // ≚ - [9359] = "\u22EE", // ⋮ - [9364] = "\u2016", // ‖ - [9369] = "\u007C", // | - [9371] = "\u2016", // ‖ - [9373] = "\u007C", // | - [9381] = "\u2223", // ∣ - [9386] = "\u007C", // | - [9396] = "\u2758", // ❘ - [9402] = "\u2240", // ≀ - [9413] = "\u200A", //   - [9416] = "\uD835\uDD19", // 𝔙 - [9419] = "\uD835\uDD33", // 𝔳 - [9424] = "\u22B2", // ⊲ - [9429] = "\u2282\u20D2", // ⊂⃒ - [9431] = "\u2283\u20D2", // ⊃⃒ - [9435] = "\uD835\uDD4D", // 𝕍 - [9439] = "\uD835\uDD67", // 𝕧 - [9444] = "\u221D", // ∝ - [9449] = "\u22B3", // ⊳ - [9453] = "\uD835\uDCB1", // 𝒱 - [9457] = "\uD835\uDCCB", // 𝓋 - [9462] = "\u2ACB\uFE00", // ⫋︀ - [9464] = "\u228A\uFE00", // ⊊︀ - [9468] = "\u2ACC\uFE00", // ⫌︀ - [9470] = "\u228B\uFE00", // ⊋︀ - [9476] = "\u22AA", // ⊪ - [9483] = "\u299A", // ⦚ - [9489] = "\u0174", // Ŵ - [9495] = "\u0175", // ŵ - [9501] = "\u2A5F", // ⩟ - [9506] = "\u22C0", // ⋀ - [9509] = "\u2227", // ∧ - [9511] = "\u2259", // ≙ - [9516] = "\u2118", // ℘ - [9519] = "\uD835\uDD1A", // 𝔚 - [9522] = "\uD835\uDD34", // 𝔴 - [9526] = "\uD835\uDD4E", // 𝕎 - [9530] = "\uD835\uDD68", // 𝕨 - [9532] = "\u2118", // ℘ - [9534] = "\u2240", // ≀ - [9539] = "\u2240", // ≀ - [9543] = "\uD835\uDCB2", // 𝒲 - [9547] = "\uD835\uDCCC", // 𝓌 - [9552] = "\u22C2", // ⋂ - [9556] = "\u25EF", // ◯ - [9559] = "\u22C3", // ⋃ - [9564] = "\u25BD", // ▽ - [9568] = "\uD835\uDD1B", // 𝔛 - [9571] = "\uD835\uDD35", // 𝔵 - [9576] = "\u27FA", // ⟺ - [9580] = "\u27F7", // ⟷ - [9582] = "\u039E", // Ξ - [9584] = "\u03BE", // ξ - [9589] = "\u27F8", // ⟸ - [9593] = "\u27F5", // ⟵ - [9597] = "\u27FC", // ⟼ - [9601] = "\u22FB", // ⋻ - [9606] = "\u2A00", // ⨀ - [9610] = "\uD835\uDD4F", // 𝕏 - [9613] = "\uD835\uDD69", // 𝕩 - [9617] = "\u2A01", // ⨁ - [9622] = "\u2A02", // ⨂ - [9627] = "\u27F9", // ⟹ - [9631] = "\u27F6", // ⟶ - [9635] = "\uD835\uDCB3", // 𝒳 - [9639] = "\uD835\uDCCD", // 𝓍 - [9644] = "\u2A06", // ⨆ - [9650] = "\u2A04", // ⨄ - [9654] = "\u25B3", // △ - [9658] = "\u22C1", // ⋁ - [9664] = "\u22C0", // ⋀ - [9670] = "\u00DD", // Ý - [9671] = "\u00DD", // Ý - [9677] = "\u00FD", // ý - [9678] = "\u00FD", // ý - [9682] = "\u042F", // Я - [9684] = "\u044F", // я - [9689] = "\u0176", // Ŷ - [9694] = "\u0177", // ŷ - [9696] = "\u042B", // Ы - [9698] = "\u044B", // ы - [9700] = "\u00A5", // ¥ - [9701] = "\u00A5", // ¥ - [9704] = "\uD835\uDD1C", // 𝔜 - [9707] = "\uD835\uDD36", // 𝔶 - [9711] = "\u0407", // Ї - [9715] = "\u0457", // ї - [9719] = "\uD835\uDD50", // 𝕐 - [9723] = "\uD835\uDD6A", // 𝕪 - [9727] = "\uD835\uDCB4", // 𝒴 - [9731] = "\uD835\uDCCE", // 𝓎 - [9735] = "\u042E", // Ю - [9739] = "\u044E", // ю - [9743] = "\u0178", // Ÿ - [9745] = "\u00FF", // ÿ - [9746] = "\u00FF", // ÿ - [9753] = "\u0179", // Ź - [9760] = "\u017A", // ź - [9766] = "\u017D", // Ž - [9772] = "\u017E", // ž - [9774] = "\u0417", // З - [9776] = "\u0437", // з - [9780] = "\u017B", // Ż - [9784] = "\u017C", // ż - [9790] = "\u2128", // ℨ - [9804] = "\u200B", // ​ - [9807] = "\u0396", // Ζ - [9810] = "\u03B6", // ζ - [9813] = "\u2128", // ℨ - [9816] = "\uD835\uDD37", // 𝔷 - [9820] = "\u0416", // Ж - [9824] = "\u0436", // ж - [9831] = "\u21DD", // ⇝ - [9835] = "\u2124", // ℤ - [9839] = "\uD835\uDD6B", // 𝕫 - [9843] = "\uD835\uDCB5", // 𝒵 - [9847] = "\uD835\uDCCF", // 𝓏 - [9850] = "\u200D", // ‍ - [9853] = "\u200C", // ‌ - }; - } - - static int BinarySearchNextState (Transition[] transitions, int state) - { - int min = 0, max = transitions.Length; - - do { - int i = min + ((max - min) / 2); - - if (state > transitions[i].From) { - min = i + 1; - } else if (state < transitions[i].From) { - max = i; - } else { - return transitions[i].To; - } - } while (min < max); - - return -1; - } - - bool PushNamedEntity (char c) - { - int next, state = states[index - 1]; - Transition[] table = null; - - switch (c) { - case '1': table = TransitionTable_1; break; - case '2': table = TransitionTable_2; break; - case '3': table = TransitionTable_3; break; - case '4': table = TransitionTable_4; break; - case '5': table = TransitionTable_5; break; - case '6': table = TransitionTable_6; break; - case '7': table = TransitionTable_7; break; - case '8': table = TransitionTable_8; break; - case ';': table = TransitionTable_semicolon; break; - case 'A': table = TransitionTable_A; break; - case 'B': table = TransitionTable_B; break; - case 'C': table = TransitionTable_C; break; - case 'D': table = TransitionTable_D; break; - case 'E': table = TransitionTable_E; break; - case 'F': table = TransitionTable_F; break; - case 'G': table = TransitionTable_G; break; - case 'H': table = TransitionTable_H; break; - case 'I': table = TransitionTable_I; break; - case 'J': table = TransitionTable_J; break; - case 'K': table = TransitionTable_K; break; - case 'L': table = TransitionTable_L; break; - case 'M': table = TransitionTable_M; break; - case 'N': table = TransitionTable_N; break; - case 'O': table = TransitionTable_O; break; - case 'P': table = TransitionTable_P; break; - case 'Q': table = TransitionTable_Q; break; - case 'R': table = TransitionTable_R; break; - case 'S': table = TransitionTable_S; break; - case 'T': table = TransitionTable_T; break; - case 'U': table = TransitionTable_U; break; - case 'V': table = TransitionTable_V; break; - case 'W': table = TransitionTable_W; break; - case 'X': table = TransitionTable_X; break; - case 'Y': table = TransitionTable_Y; break; - case 'Z': table = TransitionTable_Z; break; - case 'a': table = TransitionTable_a; break; - case 'b': table = TransitionTable_b; break; - case 'c': table = TransitionTable_c; break; - case 'd': table = TransitionTable_d; break; - case 'e': table = TransitionTable_e; break; - case 'f': table = TransitionTable_f; break; - case 'g': table = TransitionTable_g; break; - case 'h': table = TransitionTable_h; break; - case 'i': table = TransitionTable_i; break; - case 'j': table = TransitionTable_j; break; - case 'k': table = TransitionTable_k; break; - case 'l': table = TransitionTable_l; break; - case 'm': table = TransitionTable_m; break; - case 'n': table = TransitionTable_n; break; - case 'o': table = TransitionTable_o; break; - case 'p': table = TransitionTable_p; break; - case 'q': table = TransitionTable_q; break; - case 'r': table = TransitionTable_r; break; - case 's': table = TransitionTable_s; break; - case 't': table = TransitionTable_t; break; - case 'u': table = TransitionTable_u; break; - case 'v': table = TransitionTable_v; break; - case 'w': table = TransitionTable_w; break; - case 'x': table = TransitionTable_x; break; - case 'y': table = TransitionTable_y; break; - case 'z': table = TransitionTable_z; break; - default: return false; - } - - if ((next = BinarySearchNextState (table, state)) == -1) - return false; - - states[index] = next; - pushed[index] = c; - index++; - - return true; - } - - string GetNamedEntityValue () - { - int startIndex = index; - string decoded = null; - - while (startIndex > 0) { - if (NamedEntities.TryGetValue (states[startIndex - 1], out decoded)) - break; - - startIndex--; - } - - if (decoded == null) - decoded = string.Empty; - - if (startIndex < index) - decoded += new string (pushed, startIndex, index - startIndex); - - return decoded; - } - } -} diff --git a/src/MimeKit/Text/HtmlNamespace.cs b/src/MimeKit/Text/HtmlNamespace.cs deleted file mode 100644 index ee4d935..0000000 --- a/src/MimeKit/Text/HtmlNamespace.cs +++ /dev/null @@ -1,133 +0,0 @@ -// -// HtmlNamespace.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Text { - /// - /// An HTML namespace. - /// - /// - /// An HTML namespace. - /// - public enum HtmlNamespace { - /// - /// The namespace is "http://www.w3.org/1999/xhtml". - /// - Html, - - /// - /// The namespace is "http://www.w3.org/1998/Math/MathML". - /// - MathML, - - /// - /// The namespace is "http://www.w3.org/2000/svg". - /// - Svg, - - /// - /// The namespace is "http://www.w3.org/1999/xlink". - /// - XLink, - - /// - /// The namespace is "http://www.w3.org/XML/1998/namespace". - /// - Xml, - - /// - /// The namespace is "http://www.w3.org/2000/xmlns/". - /// - XmlNS - } - - /// - /// extension methods. - /// - /// - /// extension methods. - /// - static class HtmlNamespaceExtensions - { - static readonly int NamespacePrefixLength = "http://www.w3.org/".Length; - - static readonly string[] NamespaceValues = { - "http://www.w3.org/1999/xhtml", - "http://www.w3.org/1998/Math/MathML", - "http://www.w3.org/2000/svg", - "http://www.w3.org/1999/xlink", - "http://www.w3.org/XML/1998/namespace", - "http://www.w3.org/2000/xmlns/" - }; - - /// - /// Converts the enum value into the equivalent namespace url. - /// - /// - /// Converts the enum value into the equivalent namespace url. - /// - /// The tag name. - /// The enum value. - public static string ToNamespaceUrl (this HtmlNamespace value) - { - int index = (int) value; - - if (index < 0 || index >= NamespaceValues.Length) - throw new ArgumentOutOfRangeException (nameof (value)); - - return NamespaceValues[index]; - } - - /// - /// Converts the tag name into the equivalent tag id. - /// - /// - /// Converts the tag name into the equivalent tag id. - /// - /// The tag id. - /// The namespace. - public static HtmlNamespace ToHtmlNamespace (this string ns) - { - if (ns == null) - throw new ArgumentNullException (nameof (ns)); - - if (!ns.StartsWith ("http://www.w3.org/", StringComparison.OrdinalIgnoreCase)) - return HtmlNamespace.Html; - - for (int i = 0; i < NamespaceValues.Length; i++) { - if (ns.Length != NamespaceValues[i].Length) - continue; - - if (string.Compare (ns, NamespacePrefixLength, NamespaceValues[i], NamespacePrefixLength, - ns.Length - NamespacePrefixLength, StringComparison.OrdinalIgnoreCase) == 0) - return (HtmlNamespace) i; - } - - return HtmlNamespace.Html; - } - } -} diff --git a/src/MimeKit/Text/HtmlTagCallback.cs b/src/MimeKit/Text/HtmlTagCallback.cs deleted file mode 100644 index 61deac1..0000000 --- a/src/MimeKit/Text/HtmlTagCallback.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// HtmlTagCallback.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Text { - /// - /// An HTML tag callback delegate. - /// - /// - /// The delegate is called when a converter - /// is ready to write a new HTML tag, allowing developers to customize - /// whether the tag gets written at all, which attributes get written, etc. - /// - /// - /// - /// - /// The HTML tag context. - /// The HTML writer. - public delegate void HtmlTagCallback (HtmlTagContext tagContext, HtmlWriter htmlWriter); -} diff --git a/src/MimeKit/Text/HtmlTagContext.cs b/src/MimeKit/Text/HtmlTagContext.cs deleted file mode 100644 index ac36b76..0000000 --- a/src/MimeKit/Text/HtmlTagContext.cs +++ /dev/null @@ -1,223 +0,0 @@ -// -// HtmlTagContext.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; - -namespace MimeKit.Text { - /// - /// An HTML tag context. - /// - /// - /// An HTML tag context used with the delegate. - /// - /// - /// - /// - public abstract class HtmlTagContext - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The HTML tag identifier. - /// - /// is invalid. - /// - protected HtmlTagContext (HtmlTagId tagId) - { - TagId = tagId; - } - - /// - /// Get the HTML tag attributes. - /// - /// - /// Gets the HTML tag attributes. - /// - /// - /// - /// - /// The attributes. - public abstract HtmlAttributeCollection Attributes { - get; - } - - /// - /// Get or set whether or not the end tag should be deleted. - /// - /// - /// Gets or sets whether or not the end tag should be deleted. - /// - /// true if the end tag should be deleted; otherwise, false. - public bool DeleteEndTag { - get; set; - } - - /// - /// Get or set whether or not the tag should be deleted. - /// - /// - /// Gets or sets whether or not the tag should be deleted. - /// - /// true if the tag should be deleted; otherwise, false. - public bool DeleteTag { - get; set; - } - - /// - /// Get or set whether or not the should be invoked for the end tag. - /// - /// - /// Gets or sets whether or not the should be invoked for the end tag. - /// - /// - /// - /// - /// true if the callback should be invoked for end tag; otherwise, false. - public bool InvokeCallbackForEndTag { - get; set; - } - - /// - /// Get whether or not the tag is an empty element. - /// - /// - /// Gets whether or not the tag is an empty element. - /// - /// - /// - /// - /// true if the tag is an empty element; otherwise, false. - public abstract bool IsEmptyElementTag { - get; - } - - /// - /// Get whether or not the tag is an end tag. - /// - /// - /// Gets whether or not the tag is an end tag. - /// - /// - /// - /// - /// true if the tag is an end tag; otherwise, false. - public abstract bool IsEndTag { - get; - } - - /// - /// Get or set whether or not the inner content of the tag should be suppressed. - /// - /// - /// Gets or sets whether or not the inner content of the tag should be suppressed. - /// - /// true if the inner content should be suppressed; otherwise, false. - public bool SuppressInnerContent { - get; set; - } - - /// - /// Get the HTML tag identifier. - /// - /// - /// Gets the HTML tag identifier. - /// - /// - /// - /// - /// The HTML tag identifier. - public HtmlTagId TagId { - get; private set; - } - - /// - /// Get the HTML tag name. - /// - /// - /// Gets the HTML tag name. - /// - /// - /// - /// - /// The HTML tag name. - public abstract string TagName { - get; - } - - /// - /// Write the HTML tag. - /// - /// - /// Writes the HTML tag to the given . - /// - /// The HTML writer. - /// - /// is null. - /// - public void WriteTag (HtmlWriter htmlWriter) - { - WriteTag (htmlWriter, false); - } - - /// - /// Write the HTML tag. - /// - /// - /// Writes the HTML tag to the given . - /// - /// - /// - /// - /// The HTML writer. - /// true if the should also be written; otherwise, false. - /// - /// is null. - /// - public void WriteTag (HtmlWriter htmlWriter, bool writeAttributes) - { - if (htmlWriter == null) - throw new ArgumentNullException (nameof (htmlWriter)); - - if (IsEndTag) { - htmlWriter.WriteEndTag (TagName); - return; - } - - if (IsEmptyElementTag) - htmlWriter.WriteEmptyElementTag (TagName); - else - htmlWriter.WriteStartTag (TagName); - - if (writeAttributes) { - for (int i = 0; i < Attributes.Count; i++) - htmlWriter.WriteAttribute (Attributes[i]); - } - } - } -} diff --git a/src/MimeKit/Text/HtmlTagId.cs b/src/MimeKit/Text/HtmlTagId.cs deleted file mode 100644 index a6f57ce..0000000 --- a/src/MimeKit/Text/HtmlTagId.cs +++ /dev/null @@ -1,871 +0,0 @@ -// -// HtmlTagId.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Linq; -using System.Reflection; -using System.Collections.Generic; - -using MimeKit.Utils; - -namespace MimeKit.Text { - /// - /// HTML tag identifiers. - /// - /// - /// HTML tag identifiers. - /// - /// - /// - /// - public enum HtmlTagId { - /// - /// An unknown HTML tag identifier. - /// - Unknown, - - /// - /// The HTML <a> tag. - /// - A, - - /// - /// The HTML <abbr> tag. - /// - Abbr, - - /// - /// The HTML <acronym> tag. - /// - Acronym, - - /// - /// The HTML <address> tag. - /// - Address, - - /// - /// The HTML <applet> tag. - /// - Applet, - - /// - /// The HTML <area> tag. - /// - Area, - - /// - /// The HTML <article> tag. - /// - Article, - - /// - /// The HTML <aside> tag. - /// - Aside, - - /// - /// The HTML <audio> tag. - /// - Audio, - - /// - /// The HTML <b> tag. - /// - B, - - /// - /// The HTML <base> tag. - /// - Base, - - /// - /// The HTML <basefont> tag. - /// - BaseFont, - - /// - /// The HTML <bdi> tag. - /// - Bdi, - - /// - /// The HTML <bdo> tag. - /// - Bdo, - - /// - /// The HTML <bgsound> tag. - /// - BGSound, - - /// - /// The HTML <big> tag. - /// - Big, - - /// - /// The HTML <blink> tag. - /// - Blink, - - /// - /// The HTML <blockquote> tag. - /// - BlockQuote, - - /// - /// The HTML <body> tag. - /// - Body, - - /// - /// The HTML <br> tag. - /// - Br, - - /// - /// The HTML <button> tag. - /// - Button, - - /// - /// The HTML <canvas> tag. - /// - Canvas, - - /// - /// The HTML <caption> tag. - /// - Caption, - - /// - /// The HTML <center> tag. - /// - Center, - - /// - /// The HTML <cite> tag. - /// - Cite, - - /// - /// The HTML <code> tag. - /// - Code, - - /// - /// The HTML <col> tag. - /// - Col, - - /// - /// The HTML <colgroup> tag. - /// - ColGroup, - - /// - /// The HTML <command> tag. - /// - Command, - - /// - /// The HTML comment tag. - /// - Comment, - - /// - /// The HTML <datalist> tag. - /// - DataList, - - /// - /// The HTML <dd> tag. - /// - DD, - - /// - /// The HTML <del> tag. - /// - Del, - - /// - /// The HTML <details> tag. - /// - Details, - - /// - /// The HTML <dfn> tag. - /// - Dfn, - - /// - /// The HTML <dialog> tag. - /// - Dialog, - - /// - /// The HTML <dir> tag. - /// - Dir, - - /// - /// The HTML <div> tag. - /// - Div, - - /// - /// The HTML <dl> tag. - /// - DL, - - /// - /// The HTML <dt> tag. - /// - DT, - - /// - /// The HTML <em> tag. - /// - EM, - - /// - /// The HTML <embed> tag. - /// - Embed, - - /// - /// The HTML <fieldset> tag. - /// - FieldSet, - - /// - /// The HTML <figcaption> tag. - /// - FigCaption, - - /// - /// The HTML <figure> tag. - /// - Figure, - - /// - /// The HTML <font> tag. - /// - Font, - - /// - /// The HTML <footer> tag. - /// - Footer, - - /// - /// The HTML <form> tag. - /// - Form, - - /// - /// The HTML <frame> tag. - /// - Frame, - - /// - /// The HTML <frameset> tag. - /// - FrameSet, - - /// - /// The HTML <h1> tag. - /// - H1, - - /// - /// The HTML <h2> tag. - /// - H2, - - /// - /// The HTML <h3> tag. - /// - H3, - - /// - /// The HTML <h4> tag. - /// - H4, - - /// - /// The HTML <h5> tag. - /// - H5, - - /// - /// The HTML <h6> tag. - /// - H6, - - /// - /// The HTML <head> tag. - /// - Head, - - /// - /// The HTML <header> tag. - /// - Header, - - /// - /// The HTML <hr> tag. - /// - HR, - - /// - /// The HTML <html> tag. - /// - Html, - - /// - /// The HTML <i> tag. - /// - I, - - /// - /// The HTML <iframe> tag. - /// - IFrame, - - /// - /// The HTML <image> tag. - /// - [HtmlTagName ("img")] - Image, - - /// - /// The HTML <input> tag. - /// - Input, - - /// - /// The HTML <ins> tag. - /// - Ins, - - /// - /// The HTML <isindex> tag. - /// - IsIndex, - - /// - /// The HTML <kbd> tag. - /// - Kbd, - - /// - /// The HTML <keygen> tag. - /// - Keygen, - - /// - /// The HTML <label> tag. - /// - Label, - - /// - /// The HTML <legend> tag. - /// - Legend, - - /// - /// The HTML <li> tag. - /// - LI, - - /// - /// The HTML <link> tag. - /// - Link, - - /// - /// The HTML <listing> tag. - /// - Listing, - - /// - /// The HTML <main> tag. - /// - Main, - - /// - /// The HTML <map> tag. - /// - Map, - - /// - /// The HTML <mark> tag. - /// - Mark, - - /// - /// The HTML <marquee> tag. - /// - Marquee, - - /// - /// The HTML <menu> tag. - /// - Menu, - - /// - /// The HTML <menuitem> tag. - /// - MenuItem, - - /// - /// The HTML <meta> tag. - /// - Meta, - - /// - /// The HTML <meter> tag. - /// - Meter, - - /// - /// The HTML <nav> tag. - /// - Nav, - - /// - /// The HTML <nextid> tag. - /// - NextId, - - /// - /// The HTML <nobr> tag. - /// - NoBR, - - /// - /// The HTML <noembed> tag. - /// - NoEmbed, - - /// - /// The HTML <noframes> tag. - /// - NoFrames, - - /// - /// The HTML <noscript> tag. - /// - NoScript, - - /// - /// The HTML <object> tag. - /// - Object, - - /// - /// The HTML <ol> tag. - /// - OL, - - /// - /// The HTML <optgroup> tag. - /// - OptGroup, - - /// - /// The HTML <option> tag. - /// - Option, - - /// - /// The HTML <output> tag. - /// - Output, - - /// - /// The HTML <p> tag. - /// - P, - - /// - /// The HTML <param> tag. - /// - Param, - - /// - /// The HTML <plaintext> tag. - /// - PlainText, - - /// - /// The HTML <pre> tag. - /// - Pre, - - /// - /// The HTML <progress> tag. - /// - Progress, - - /// - /// The HTML <q> tag. - /// - Q, - - /// - /// The HTML <rp> tag. - /// - RP, - - /// - /// The HTML <rt> tag. - /// - RT, - - /// - /// The HTML <ruby> tag. - /// - Ruby, - - /// - /// The HTML <s> tag. - /// - S, - - /// - /// The HTML <samp> tag. - /// - Samp, - - /// - /// The HTML <script> tag. - /// - Script, - - /// - /// The HTML <section> tag. - /// - Section, - - /// - /// The HTML <select> tag. - /// - Select, - - /// - /// The HTML <small> tag. - /// - Small, - - /// - /// The HTML <source> tag. - /// - Source, - - /// - /// The HTML <span> tag. - /// - Span, - - /// - /// The HTML <strike> tag. - /// - Strike, - - /// - /// The HTML <strong> tag. - /// - Strong, - - /// - /// The HTML <style> tag. - /// - Style, - - /// - /// The HTML <sub> tag. - /// - Sub, - - /// - /// The HTML <summary> tag. - /// - Summary, - - /// - /// The HTML <sup> tag. - /// - Sup, - - /// - /// The HTML <table> tag. - /// - Table, - - /// - /// The HTML <tbody> tag. - /// - TBody, - - /// - /// The HTML <td> tag. - /// - TD, - - /// - /// The HTML <textarea> tag. - /// - TextArea, - - /// - /// The HTML <tfoot> tag. - /// - Tfoot, - - /// - /// The HTML <th> tag. - /// - TH, - - /// - /// The HTML <thead> tag. - /// - THead, - - /// - /// The HTML <time> tag. - /// - Time, - - /// - /// The HTML <title> tag. - /// - Title, - - /// - /// The HTML <tr> tag. - /// - TR, - - /// - /// The HTML <track> tag. - /// - Track, - - /// - /// The HTML <tt> tag. - /// - TT, - - /// - /// The HTML <u> tag. - /// - U, - - /// - /// The HTML <ul> tag. - /// - UL, - - /// - /// The HTML <var> tag. - /// - Var, - - /// - /// The HTML <video> tag. - /// - Video, - - /// - /// The HTML <wbr> tag. - /// - Wbr, - - /// - /// The HTML <xml> tag. - /// - Xml, - - /// - /// The HTML <xmp> tag. - /// - Xmp, - } - - [AttributeUsage (AttributeTargets.Field)] - class HtmlTagNameAttribute : Attribute { - public HtmlTagNameAttribute (string name) - { - Name = name; - } - - public string Name { - get; protected set; - } - } - - /// - /// extension methods. - /// - /// - /// extension methods. - /// - public static class HtmlTagIdExtensions - { - static readonly Dictionary TagNameToId; - - static HtmlTagIdExtensions () - { - var values = (HtmlTagId[]) Enum.GetValues (typeof (HtmlTagId)); - - TagNameToId = new Dictionary (values.Length - 1, MimeUtils.OrdinalIgnoreCase); - - for (int i = 0; i < values.Length - 1; i++) - TagNameToId.Add (values[i].ToHtmlTagName (), values[i]); - } - - /// - /// Converts the enum value into the equivalent tag name. - /// - /// - /// Converts the enum value into the equivalent tag name. - /// - /// The tag name. - /// The enum value. - public static string ToHtmlTagName (this HtmlTagId value) - { - if (value == HtmlTagId.Comment) - return "!"; - - var name = value.ToString (); - -#if NETSTANDARD1_3 || NETSTANDARD1_6 - var field = typeof (HtmlTagId).GetTypeInfo ().GetDeclaredField (name); - var attrs = field.GetCustomAttributes (typeof (HtmlTagNameAttribute), false).ToArray (); -#else - var field = typeof (HtmlTagId).GetField (name); - var attrs = field.GetCustomAttributes (typeof (HtmlTagNameAttribute), false); -#endif - - if (attrs != null && attrs.Length == 1) - return ((HtmlTagNameAttribute) attrs[0]).Name; - - return name.ToLowerInvariant (); - } - - /// - /// Converts the tag name into the equivalent tag id. - /// - /// - /// Converts the tag name into the equivalent tag id. - /// - /// The tag id. - /// The tag name. - internal static HtmlTagId ToHtmlTagId (this string name) - { - HtmlTagId value; - - if (string.IsNullOrEmpty (name)) - return HtmlTagId.Unknown; - - if (name[0] == '!') - return HtmlTagId.Comment; - - if (!TagNameToId.TryGetValue (name, out value)) - return HtmlTagId.Unknown; - - return value; - } - - /// - /// Determines whether or not the HTML tag is an empty element. - /// - /// - /// Determines whether or not the HTML tag is an empty element. - /// - /// true if the tag is an empty element; otherwise, false. - /// Identifier. - public static bool IsEmptyElement (this HtmlTagId id) - { - switch (id) { - case HtmlTagId.Area: - case HtmlTagId.Base: - case HtmlTagId.Br: - case HtmlTagId.Col: - case HtmlTagId.Command: - case HtmlTagId.Embed: - case HtmlTagId.HR: - case HtmlTagId.Image: - case HtmlTagId.Input: - case HtmlTagId.Keygen: - case HtmlTagId.Link: - case HtmlTagId.Meta: - case HtmlTagId.Param: - case HtmlTagId.Source: - case HtmlTagId.Track: - case HtmlTagId.Wbr: - return true; - default: - return false; - } - } - - /// - /// Determines whether or not the HTML tag is a formatting element. - /// - /// - /// Determines whether or not the HTML tag is a formatting element. - /// - /// true if the HTML tag is a formatting element; otherwise, false. - /// The HTML tag identifier. - public static bool IsFormattingElement (this HtmlTagId id) - { - switch (id) { - case HtmlTagId.A: - case HtmlTagId.B: - case HtmlTagId.Big: - case HtmlTagId.Code: - case HtmlTagId.EM: - case HtmlTagId.Font: - case HtmlTagId.I: - case HtmlTagId.NoBR: - case HtmlTagId.S: - case HtmlTagId.Small: - case HtmlTagId.Strike: - case HtmlTagId.Strong: - case HtmlTagId.TT: - case HtmlTagId.U: - return true; - default: - return false; - } - } - } -} diff --git a/src/MimeKit/Text/HtmlTextPreviewer.cs b/src/MimeKit/Text/HtmlTextPreviewer.cs deleted file mode 100644 index b3e2e11..0000000 --- a/src/MimeKit/Text/HtmlTextPreviewer.cs +++ /dev/null @@ -1,253 +0,0 @@ -// -// HtmlTextPreviewer.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace MimeKit.Text { - /// - /// A text previewer for HTML content. - /// - /// - /// A text previewer for HTML content. - /// - public class HtmlTextPreviewer : TextPreviewer - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new previewer for HTML. - /// - public HtmlTextPreviewer () - { - } - - /// - /// Get the input format. - /// - /// - /// Gets the input format. - /// - /// The input format. - public override TextFormat InputFormat { - get { return TextFormat.Html; } - } - - static bool IsWhiteSpace (char c) - { - return char.IsWhiteSpace (c) || (c >= 0x200B && c <= 0x200D); - } - - static bool Append (char[] preview, ref int previewLength, string value, ref bool lwsp) - { - int i; - - for (i = 0; i < value.Length && previewLength < preview.Length; i++) { - if (IsWhiteSpace (value[i])) { - if (!lwsp) { - preview[previewLength++] = ' '; - lwsp = true; - } - } else { - preview[previewLength++] = value[i]; - lwsp = false; - } - } - - if (i < value.Length) { - if (lwsp) - previewLength--; - - preview[previewLength - 1] = '\u2026'; - lwsp = false; - return true; - } - - return false; - } - - sealed class HtmlTagContext - { - public HtmlTagContext (HtmlTagId id) - { - TagId = id; - } - - public HtmlTagId TagId { - get; - } - - public int ListIndex { - get; set; - } - - public bool SuppressInnerContent { - get; set; - } - } - - static void Pop (IList stack, HtmlTagId id) - { - for (int i = stack.Count; i > 0; i--) { - if (stack[i - 1].TagId == id) { - stack.RemoveAt (i - 1); - break; - } - } - } - - static bool ShouldSuppressInnerContent (HtmlTagId id) - { - switch (id) { - case HtmlTagId.OL: - case HtmlTagId.Script: - case HtmlTagId.Style: - case HtmlTagId.Table: - case HtmlTagId.TBody: - case HtmlTagId.THead: - case HtmlTagId.TR: - case HtmlTagId.UL: - return true; - default: - return false; - } - } - - static bool SuppressContent (IList stack) - { - int lastIndex = stack.Count - 1; - - return lastIndex >= 0 && stack[lastIndex].SuppressInnerContent; - } - - HtmlTagContext GetListItemContext (IList stack) - { - for (int i = stack.Count; i > 0; i--) { - var ctx = stack[i - 1]; - - if (ctx.TagId == HtmlTagId.OL || ctx.TagId == HtmlTagId.UL) - return ctx; - } - - return null; - } - - /// - /// Get a text preview of a stream of text. - /// - /// - /// Gets a text preview of a stream of text. - /// - /// The original text stream. - /// A string representing a shortened preview of the original text. - /// - /// is null. - /// - public override string GetPreviewText (TextReader reader) - { - if (reader == null) - throw new ArgumentNullException (nameof (reader)); - - var tokenizer = new HtmlTokenizer (reader) { IgnoreTruncatedTags = true }; - var preview = new char[MaximumPreviewLength]; - var stack = new List (); - var prefix = string.Empty; - int previewLength = 0; - HtmlTagContext ctx; - HtmlAttribute attr; - bool body = false; - bool full = false; - bool lwsp = true; - HtmlToken token; - - while (!full && tokenizer.ReadNextToken (out token)) { - switch (token.Kind) { - case HtmlTokenKind.Tag: - var tag = (HtmlTagToken) token; - - if (!tag.IsEndTag) { - if (body) { - switch (tag.Id) { - case HtmlTagId.Image: - if ((attr = tag.Attributes.FirstOrDefault (x => x.Id == HtmlAttributeId.Alt)) != null) { - full = Append (preview, ref previewLength, prefix + attr.Value, ref lwsp); - prefix = string.Empty; - } - break; - case HtmlTagId.LI: - if ((ctx = GetListItemContext (stack)) != null) { - if (ctx.TagId == HtmlTagId.OL) { - full = Append (preview, ref previewLength, $" {++ctx.ListIndex}. ", ref lwsp); - prefix = string.Empty; - } else { - //full = Append (preview, ref previewLength, " \u2022 ", ref lwsp); - prefix = " "; - } - } - break; - case HtmlTagId.Br: - case HtmlTagId.P: - prefix = " "; - break; - } - - if (!tag.IsEmptyElement) { - ctx = new HtmlTagContext (tag.Id) { - SuppressInnerContent = ShouldSuppressInnerContent (tag.Id) - }; - stack.Add (ctx); - } - } else if (tag.Id == HtmlTagId.Body && !tag.IsEmptyElement) { - body = true; - } - } else if (tag.Id == HtmlTagId.Body) { - stack.Clear (); - body = false; - } else { - Pop (stack, tag.Id); - } - break; - case HtmlTokenKind.Data: - if (body && !SuppressContent (stack)) { - var data = (HtmlDataToken) token; - - full = Append (preview, ref previewLength, prefix + data.Data, ref lwsp); - prefix = string.Empty; - } - break; - } - } - - if (lwsp && previewLength > 0) - previewLength--; - - return new string (preview, 0, previewLength); - } - } -} diff --git a/src/MimeKit/Text/HtmlToHtml.cs b/src/MimeKit/Text/HtmlToHtml.cs deleted file mode 100644 index c9b4abf..0000000 --- a/src/MimeKit/Text/HtmlToHtml.cs +++ /dev/null @@ -1,357 +0,0 @@ -// -// HtmlToHtml.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections.Generic; - -namespace MimeKit.Text { - /// - /// An HTML to HTML converter. - /// - /// - /// Used to convert HTML into HTML. - /// - /// - /// - /// - public class HtmlToHtml : TextConverter - { - //static readonly HashSet AutoClosingTags; - - //static HtmlToHtml () - //{ - // // Note: These are tags that auto-close when an identical tag is encountered and/or when a parent node is closed. - // AutoClosingTags = new HashSet (new [] { - // "li", - // "p", - // "td", - // "tr" - // }, MimeUtils.OrdinalIgnoreCase); - //} - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new HTML to HTML converter. - /// - public HtmlToHtml () - { - } - - /// - /// Get the input format. - /// - /// - /// Gets the input format. - /// - /// The input format. - public override TextFormat InputFormat { - get { return TextFormat.Html; } - } - - /// - /// Get the output format. - /// - /// - /// Gets the output format. - /// - /// The output format. - public override TextFormat OutputFormat { - get { return TextFormat.Html; } - } - - /// - /// Get or set whether or not the converter should remove HTML comments from the output. - /// - /// - /// Gets or sets whether or not the converter should remove HTML comments from the output. - /// - /// true if the converter should remove comments; otherwise, false. - public bool FilterComments { - get; set; - } - - /// - /// Get or set whether or not executable scripts should be stripped from the output. - /// - /// - /// Gets or sets whether or not executable scripts should be stripped from the output. - /// - /// true if executable scripts should be filtered; otherwise, false. - public bool FilterHtml { - get; set; - } - - /// - /// Get or set the footer format. - /// - /// - /// Gets or sets the footer format. - /// - /// The footer format. - public HeaderFooterFormat FooterFormat { - get; set; - } - - /// - /// Get or set the header format. - /// - /// - /// Gets or sets the header format. - /// - /// The header format. - public HeaderFooterFormat HeaderFormat { - get; set; - } - - /// - /// Get or set the method to use for custom - /// filtering of HTML tags and content. - /// - /// - /// Get or set the method to use for custom - /// filtering of HTML tags and content. - /// - /// - /// - /// - /// The html tag callback. - public HtmlTagCallback HtmlTagCallback { - get; set; - } - -#if false - /// - /// Get or set whether or not the converter should collapse white space, - /// balance tags, and fix other problems in the source HTML. - /// - /// - /// Gets or sets whether or not the converter should collapse white space, - /// balance tags, and fix other problems in the source HTML. - /// - /// true if the output html should be normalized; otherwise, false. - public bool NormalizeHtml { - get; set; - } -#endif - -#if false - /// - /// Get or set whether or not the converter should only output an HTML fragment. - /// - /// - /// Gets or sets whether or not the converter should only output an HTML fragment. - /// - /// true if the converter should only output an HTML fragment; otherwise, false. - public bool OutputHtmlFragment { - get; set; - } -#endif - - class HtmlToHtmlTagContext : HtmlTagContext - { - readonly HtmlTagToken tag; - - public HtmlToHtmlTagContext (HtmlTagToken htmlTag) : base (htmlTag.Id) - { - tag = htmlTag; - } - - public override string TagName { - get { return tag.Name; } - } - - public override HtmlAttributeCollection Attributes { - get { return tag.Attributes; } - } - - public override bool IsEmptyElementTag { - get { return tag.IsEmptyElement || tag.Id.IsEmptyElement (); } - } - - public override bool IsEndTag { - get { return tag.IsEndTag; } - } - } - - static void DefaultHtmlTagCallback (HtmlTagContext tagContext, HtmlWriter htmlWriter) - { - tagContext.WriteTag (htmlWriter, true); - } - - static bool SuppressContent (IList stack) - { - for (int i = stack.Count; i > 0; i--) { - if (stack[i - 1].SuppressInnerContent) - return true; - } - - return false; - } - - static HtmlToHtmlTagContext Pop (IList stack, string name) - { - for (int i = stack.Count; i > 0; i--) { - if (stack[i - 1].TagName.Equals (name, StringComparison.OrdinalIgnoreCase)) { - var ctx = stack[i - 1]; - stack.RemoveAt (i - 1); - return ctx; - } - } - - return null; - } - - /// - /// Convert the contents of from the to the - /// and uses the to write the resulting text. - /// - /// - /// Converts the contents of from the to the - /// and uses the to write the resulting text. - /// - /// The text reader. - /// The text writer. - /// - /// is null. - /// -or- - /// is null. - /// - public override void Convert (TextReader reader, TextWriter writer) - { - if (reader == null) - throw new ArgumentNullException (nameof (reader)); - - if (writer == null) - throw new ArgumentNullException (nameof (writer)); - - if (!string.IsNullOrEmpty (Header)) { - if (HeaderFormat == HeaderFooterFormat.Text) { - var converter = new TextToHtml { OutputHtmlFragment = true }; - - using (var sr = new StringReader (Header)) - converter.Convert (sr, writer); - } else { - writer.Write (Header); - } - } - - using (var htmlWriter = new HtmlWriter (writer)) { - var callback = HtmlTagCallback ?? DefaultHtmlTagCallback; - var stack = new List (); - var tokenizer = new HtmlTokenizer (reader); - HtmlToHtmlTagContext ctx; - HtmlToken token; - - while (tokenizer.ReadNextToken (out token)) { - switch (token.Kind) { - default: - if (!SuppressContent (stack)) - htmlWriter.WriteToken (token); - break; - case HtmlTokenKind.Comment: - if (!FilterComments && !SuppressContent (stack)) - htmlWriter.WriteToken (token); - break; - case HtmlTokenKind.Tag: - var tag = (HtmlTagToken) token; - - if (!tag.IsEndTag) { - //if (NormalizeHtml && AutoClosingTags.Contains (startTag.TagName) && - // (ctx = Pop (stack, startTag.TagName)) != null && - // ctx.InvokeCallbackForEndTag && !SuppressContent (stack)) { - // var value = string.Format ("", ctx.TagName); - // var name = ctx.TagName; - // - // ctx = new HtmlToHtmlTagContext (new HtmlTokenTag (HtmlTokenKind.EndTag, name, value)) { - // InvokeCallbackForEndTag = ctx.InvokeCallbackForEndTag, - // SuppressInnerContent = ctx.SuppressInnerContent, - // DeleteEndTag = ctx.DeleteEndTag, - // DeleteTag = ctx.DeleteTag - // }; - // callback (ctx, htmlWriter); - //} - - if (!tag.IsEmptyElement) { - ctx = new HtmlToHtmlTagContext (tag); - - if (FilterHtml && ctx.TagId == HtmlTagId.Script) { - ctx.SuppressInnerContent = true; - ctx.DeleteEndTag = true; - ctx.DeleteTag = true; - } else if (!SuppressContent (stack)) { - callback (ctx, htmlWriter); - } - - stack.Add (ctx); - } else if (!SuppressContent (stack)) { - ctx = new HtmlToHtmlTagContext (tag); - - if (!FilterHtml || ctx.TagId != HtmlTagId.Script) - callback (ctx, htmlWriter); - } - } else { - if ((ctx = Pop (stack, tag.Name)) != null) { - if (!SuppressContent (stack)) { - if (ctx.InvokeCallbackForEndTag) { - ctx = new HtmlToHtmlTagContext (tag) { - InvokeCallbackForEndTag = ctx.InvokeCallbackForEndTag, - SuppressInnerContent = ctx.SuppressInnerContent, - DeleteEndTag = ctx.DeleteEndTag, - DeleteTag = ctx.DeleteTag - }; - callback (ctx, htmlWriter); - } else if (!ctx.DeleteEndTag) { - htmlWriter.WriteEndTag (tag.Name); - } - } - } else if (!SuppressContent (stack)) { - ctx = new HtmlToHtmlTagContext (tag); - callback (ctx, htmlWriter); - } - } - break; - } - } - - htmlWriter.Flush (); - } - - if (!string.IsNullOrEmpty (Footer)) { - if (FooterFormat == HeaderFooterFormat.Text) { - var converter = new TextToHtml { OutputHtmlFragment = true }; - - using (var sr = new StringReader (Footer)) - converter.Convert (sr, writer); - } else { - writer.Write (Footer); - } - } - } - } -} diff --git a/src/MimeKit/Text/HtmlToken.cs b/src/MimeKit/Text/HtmlToken.cs deleted file mode 100644 index fd85943..0000000 --- a/src/MimeKit/Text/HtmlToken.cs +++ /dev/null @@ -1,656 +0,0 @@ -// -// HtmlToken.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.IO; -using System.Collections.Generic; - -namespace MimeKit.Text { - /// - /// An abstract HTML token class. - /// - /// - /// An abstract HTML token class. - /// - public abstract class HtmlToken - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The kind of token. - protected HtmlToken (HtmlTokenKind kind) - { - Kind = kind; - } - - /// - /// Get the kind of HTML token that this object represents. - /// - /// - /// Gets the kind of HTML token that this object represents. - /// - /// The kind of token. - public HtmlTokenKind Kind { - get; private set; - } - - /// - /// Write the HTML token to a . - /// - /// - /// Writes the HTML token to a . - /// - /// The output. - /// - /// is null. - /// - public abstract void WriteTo (TextWriter output); - - /// - /// Returns a that represents the current . - /// - /// - /// Returns a that represents the current . - /// - /// A that represents the current . - public override string ToString () - { - using (var output = new StringWriter ()) { - WriteTo (output); - - return output.ToString (); - } - } - } - - /// - /// An HTML comment token. - /// - /// - /// An HTML comment token. - /// - public class HtmlCommentToken : HtmlToken - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The comment text. - /// true if the comment is bogus; otherwise, false. - /// - /// is null. - /// - public HtmlCommentToken (string comment, bool bogus = false) : base (HtmlTokenKind.Comment) - { - if (comment == null) - throw new ArgumentNullException (nameof (comment)); - - IsBogusComment = bogus; - Comment = comment; - } - - /// - /// Get the comment. - /// - /// - /// Gets the comment. - /// - /// The comment. - public string Comment { - get; private set; - } - - /// - /// Get whether or not the comment is a bogus comment. - /// - /// - /// Gets whether or not the comment is a bogus comment. - /// - /// true if the comment is bogus; otherwise, false. - public bool IsBogusComment { - get; private set; - } - - internal bool IsBangComment { - get; set; - } - - /// - /// Write the HTML comment to a . - /// - /// - /// Writes the HTML comment to a . - /// - /// The output. - /// - /// is null. - /// - public override void WriteTo (TextWriter output) - { - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (!IsBogusComment) { - output.Write (""); - } else { - output.Write ('<'); - if (IsBangComment) - output.Write ('!'); - output.Write (Comment); - output.Write ('>'); - } - } - } - - /// - /// An HTML token constisting of character data. - /// - /// - /// An HTML token consisting of character data. - /// - public class HtmlDataToken : HtmlToken - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The kind of character data. - /// The character data. - /// - /// is not a valid . - /// - /// - /// is null. - /// - protected HtmlDataToken (HtmlTokenKind kind, string data) : base (kind) - { - switch (kind) { - default: throw new ArgumentOutOfRangeException (nameof (kind)); - case HtmlTokenKind.ScriptData: - case HtmlTokenKind.CData: - case HtmlTokenKind.Data: - break; - } - - if (data == null) - throw new ArgumentNullException (nameof (data)); - - Data = data; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The character data. - /// - /// is null. - /// - public HtmlDataToken (string data) : base (HtmlTokenKind.Data) - { - if (data == null) - throw new ArgumentNullException (nameof (data)); - - Data = data; - } - - internal bool EncodeEntities { - get; set; - } - - /// - /// Get the character data. - /// - /// - /// Gets the character data. - /// - /// The character data. - public string Data { - get; private set; - } - - /// - /// Write the HTML character data to a . - /// - /// - /// Writes the HTML character data to a , - /// encoding it if it isn't already encoded. - /// - /// The output. - /// - /// is null. - /// - public override void WriteTo (TextWriter output) - { - if (output == null) - throw new ArgumentNullException (nameof (output)); - - if (!EncodeEntities) { - output.Write (Data); - return; - } - - HtmlUtils.HtmlEncode (output, Data); - } - } - - /// - /// An HTML token constisting of [CDATA[. - /// - /// - /// An HTML token consisting of [CDATA[. - /// - public class HtmlCDataToken : HtmlDataToken - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The character data. - /// - /// is null. - /// - public HtmlCDataToken (string data) : base (HtmlTokenKind.CData, data) - { - } - - /// - /// Write the HTML character data to a . - /// - /// - /// Writes the HTML character data to a , - /// encoding it if it isn't already encoded. - /// - /// The output. - /// - /// is null. - /// - public override void WriteTo (TextWriter output) - { - if (output == null) - throw new ArgumentNullException (nameof (output)); - - output.Write (""); - } - } - - /// - /// An HTML token constisting of script data. - /// - /// - /// An HTML token consisting of script data. - /// - public class HtmlScriptDataToken : HtmlDataToken - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The script data. - /// - /// is null. - /// - public HtmlScriptDataToken (string data) : base (HtmlTokenKind.ScriptData, data) - { - } - - /// - /// Write the HTML script data to a . - /// - /// - /// Writes the HTML script data to a , - /// encoding it if it isn't already encoded. - /// - /// The output. - /// - /// is null. - /// - public override void WriteTo (TextWriter output) - { - if (output == null) - throw new ArgumentNullException (nameof (output)); - - output.Write (Data); - } - } - - /// - /// An HTML tag token. - /// - /// - /// An HTML tag token. - /// - public class HtmlTagToken : HtmlToken - { - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The name of the tag. - /// The attributes. - /// true if the tag is an empty element; otherwise, false. - /// - /// is null. - /// -or- - /// is null. - /// - public HtmlTagToken (string name, IEnumerable attributes, bool isEmptyElement) : base (HtmlTokenKind.Tag) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - if (attributes == null) - throw new ArgumentNullException (nameof (attributes)); - - Attributes = new HtmlAttributeCollection (attributes); - IsEmptyElement = isEmptyElement; - Id = name.ToHtmlTagId (); - Name = name; - } - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The name of the tag. - /// true if the tag is an end tag; otherwise, false. - /// - /// is null. - /// - public HtmlTagToken (string name, bool isEndTag) : base (HtmlTokenKind.Tag) - { - if (name == null) - throw new ArgumentNullException (nameof (name)); - - Attributes = new HtmlAttributeCollection (); - Id = name.ToHtmlTagId (); - IsEndTag = isEndTag; - Name = name; - } - - /// - /// Get the attributes. - /// - /// - /// Gets the attributes. - /// - /// The attributes. - public HtmlAttributeCollection Attributes { - get; private set; - } - - /// - /// Get the HTML tag identifier. - /// - /// - /// Gets the HTML tag identifier. - /// - /// The HTML tag identifier. - public HtmlTagId Id { - get; private set; - } - - /// - /// Get whether or not the tag is an empty element. - /// - /// - /// Gets whether or not the tag is an empty element. - /// - /// true if the tag is an empty element; otherwise, false. - public bool IsEmptyElement { - get; internal set; - } - - /// - /// Get whether or not the tag is an end tag. - /// - /// - /// Gets whether or not the tag is an end tag. - /// - /// true if the tag is an end tag; otherwise, false. - public bool IsEndTag { - get; private set; - } - - /// - /// Get the name of the tag. - /// - /// - /// Gets the name of the tag. - /// - /// The name. - public string Name { - get; private set; - } - - /// - /// Write the HTML tag to a . - /// - /// - /// Writes the HTML tag to a . - /// - /// The output. - /// - /// is null. - /// - public override void WriteTo (TextWriter output) - { - if (output == null) - throw new ArgumentNullException (nameof (output)); - - output.Write ('<'); - if (IsEndTag) - output.Write ('/'); - output.Write (Name); - for (int i = 0; i < Attributes.Count; i++) { - output.Write (' '); - output.Write (Attributes[i].Name); - if (Attributes[i].Value != null) { - output.Write ('='); - HtmlUtils.HtmlAttributeEncode (output, Attributes[i].Value); - } - } - if (IsEmptyElement) - output.Write ('/'); - output.Write ('>'); - } - } - - /// - /// An HTML DOCTYPE token. - /// - /// - /// An HTML DOCTYPE token. - /// - public class HtmlDocTypeToken : HtmlToken - { - string publicIdentifier; - string systemIdentifier; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - public HtmlDocTypeToken () : base (HtmlTokenKind.DocType) - { - RawTagName = "DOCTYPE"; - } - - internal string RawTagName { - get; set; - } - - /// - /// Get whether or not quirks-mode should be forced. - /// - /// - /// Gets whether or not quirks-mode should be forced. - /// - /// true if quirks-mode should be forced; otherwise, false. - public bool ForceQuirksMode { - get; set; - } - - /// - /// Get or set the DOCTYPE name. - /// - /// - /// Gets or sets the DOCTYPE name. - /// - /// The name. - public string Name { - get; set; - } - - /// - /// Get or set the public identifier. - /// - /// - /// Gets or sets the public identifier. - /// - /// The public identifier. - public string PublicIdentifier { - get { return publicIdentifier; } - set { - publicIdentifier = value; - if (value != null) { - if (PublicKeyword == null) - PublicKeyword = "PUBLIC"; - } else { - if (systemIdentifier != null) - SystemKeyword = "SYSTEM"; - } - } - } - - /// - /// Get the public keyword that was used. - /// - /// - /// Gets the public keyword that was used. - /// - /// The public keyword or null if it wasn't used. - public string PublicKeyword { - get; internal set; - } - - /// - /// Get or set the system identifier. - /// - /// - /// Gets or sets the system identifier. - /// - /// The system identifier. - public string SystemIdentifier { - get { return systemIdentifier; } - set { - systemIdentifier = value; - if (value != null) { - if (publicIdentifier == null && SystemKeyword == null) - SystemKeyword = "SYSTEM"; - } else { - SystemKeyword = null; - } - } - } - - /// - /// Get the system keyword that was used. - /// - /// - /// Gets the system keyword that was used. - /// - /// The system keyword or null if it wasn't used. - public string SystemKeyword { - get; internal set; - } - - /// - /// Write the DOCTYPE tag to a . - /// - /// - /// Writes the DOCTYPE tag to a . - /// - /// The output. - /// - /// is null. - /// - public override void WriteTo (TextWriter output) - { - if (output == null) - throw new ArgumentNullException (nameof (output)); - - output.Write ("'); - } - } -} diff --git a/src/MimeKit/Text/HtmlTokenKind.cs b/src/MimeKit/Text/HtmlTokenKind.cs deleted file mode 100644 index 0ff55fa..0000000 --- a/src/MimeKit/Text/HtmlTokenKind.cs +++ /dev/null @@ -1,65 +0,0 @@ -// -// HtmlTokenKind.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -namespace MimeKit.Text { - /// - /// The kinds of tokens that the can emit. - /// - /// - /// The kinds of tokens that the can emit. - /// - public enum HtmlTokenKind { - /// - /// A token consisting of [CDATA[. - /// - CData, - - /// - /// An HTML comment token. - /// - Comment, - - /// - /// A token consisting of character data. - /// - Data, - - /// - /// An HTML DOCTYPE token. - /// - DocType, - - /// - /// A token consisting of script data. - /// - ScriptData, - - /// - /// An HTML tag token. - /// - Tag, - } -} diff --git a/src/MimeKit/Text/HtmlTokenizer.cs b/src/MimeKit/Text/HtmlTokenizer.cs deleted file mode 100644 index b414052..0000000 --- a/src/MimeKit/Text/HtmlTokenizer.cs +++ /dev/null @@ -1,2937 +0,0 @@ -// -// HtmlTokenizer.cs -// -// Author: Jeffrey Stedfast -// -// Copyright (c) 2013-2020 .NET Foundation and Contributors -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System.IO; -using System.Runtime.CompilerServices; - -namespace MimeKit.Text { - /// - /// An HTML tokenizer. - /// - /// - /// Tokenizes HTML text, emitting an for each token it encounters. - /// - public class HtmlTokenizer - { - // Specification: https://dev.w3.org/html5/spec-LC/tokenization.html - const string DocType = "doctype"; - const string CData = "[CDATA["; - - readonly HtmlEntityDecoder entity = new HtmlEntityDecoder (); - readonly CharBuffer data = new CharBuffer (2048); - readonly CharBuffer name = new CharBuffer (32); - readonly char[] cdata = new char[3]; - readonly TextReader text; - HtmlDocTypeToken doctype; - HtmlAttribute attribute; - string activeTagName; - HtmlTagToken tag; - int cdataIndex; - bool isEndTag; - bool bang; - char quote; - - /// - /// Initialize a new instance of the class. - /// - /// - /// Creates a new . - /// - /// The . - public HtmlTokenizer (TextReader reader) - { - DecodeCharacterReferences = true; - LinePosition = 1; - LineNumber = 1; - text = reader; - } - - /// - /// Get or set whether or not the tokenizer should decode character references. - /// - /// - /// Gets or sets whether or not the tokenizer should decode character references. - /// Character references in attribute values will still be decoded - /// even if this value is set to false. - /// - /// true if character references should be decoded; otherwise, false. - public bool DecodeCharacterReferences { - get; set; - } - - /// - /// Get the current HTML namespace detected by the tokenizer. - /// - /// - /// Gets the current HTML namespace detected by the tokenizer. - /// - /// The html namespace. - public HtmlNamespace HtmlNamespace { - get; private set; - } - - /// - /// Get or set whether or not the tokenizer should ignore truncated tags. - /// - /// - /// Gets or sets whether or not the tokenizer should ignore truncated tags. - /// If false and the stream abrubtly ends in the middle of an HTML tag, it will be - /// treated as an instead. - /// - /// true if truncated tags should be ignored; otherwise, false. - public bool IgnoreTruncatedTags { - get; set; - } - - /// - /// Gets the current line number. - /// - /// - /// This property is most commonly used for error reporting, but can be called - /// at any time. The starting value for this property is 1. - /// Combined with , a value of 1,1 indicates - /// the start of the document. - /// - /// The current line number. - public int LineNumber { - get; private set; - } - - /// - /// Gets the current line position. - /// - /// - /// This property is most commonly used for error reporting, but can be called - /// at any time. The starting value for this property is 1. - /// Combined with , a value of 1,1 indicates - /// the start of the document. - /// - /// The column position of the current line. - public int LinePosition { - get; private set; - } - - /// - /// Get the current state of the tokenizer. - /// - /// - /// Gets the current state of the tokenizer. - /// - /// The current state of the tokenizer. - public HtmlTokenizerState TokenizerState { - get; private set; - } - - /// - /// Create a DOCTYPE token. - /// - /// - /// Creates a DOCTYPE token. - /// - /// The DOCTYPE token. - protected virtual HtmlDocTypeToken CreateDocType () - { - return new HtmlDocTypeToken (); - } - - HtmlDocTypeToken CreateDocTypeToken (string rawTagName) - { - var token = CreateDocType (); - token.RawTagName = rawTagName; - return token; - } - - /// - /// Create an HTML comment token. - /// - /// - /// Creates an HTML comment token. - /// - /// The HTML comment token. - /// The comment. - /// true if the comment is bogus; otherwise, false. - protected virtual HtmlCommentToken CreateCommentToken (string comment, bool bogus = false) - { - return new HtmlCommentToken (comment, bogus); - } - - /// - /// Create an HTML character data token. - /// - /// - /// Creates an HTML character data token. - /// - /// The HTML character data token. - /// The character data. - protected virtual HtmlDataToken CreateDataToken (string data) - { - return new HtmlDataToken (data); - } - - /// - /// Create an HTML character data token. - /// - /// - /// Creates an HTML character data token. - /// - /// The HTML character data token. - /// The character data. - protected virtual HtmlCDataToken CreateCDataToken (string data) - { - return new HtmlCDataToken (data); - } - - /// - /// Create an HTML script data token. - /// - /// - /// Creates an HTML script data token. - /// - /// The HTML script data token. - /// The script data. - protected virtual HtmlScriptDataToken CreateScriptDataToken (string data) - { - return new HtmlScriptDataToken (data); - } - - /// - /// Create an HTML tag token. - /// - /// - /// Creates an HTML tag token. - /// - /// The HTML tag token. - /// The tag name. - /// true if the tag is an end tag; otherwise, false. - protected virtual HtmlTagToken CreateTagToken (string name, bool isEndTag = false) - { - return new HtmlTagToken (name, isEndTag); - } - - /// - /// Create an attribute. - /// - /// - /// Creates an attribute. - /// - /// The attribute. - /// The attribute name. - protected virtual HtmlAttribute CreateAttribute (string name) - { - return new HtmlAttribute (name); - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - static bool IsAlphaNumeric (int c) - { - return ((uint) (c - 'A') <= 'Z' - 'A') || ((uint) (c - 'a') <= 'z' - 'a') || ((uint) (c - '0') <= '9' - '0'); - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - static bool IsAsciiLetter (int c) - { - return ((uint) (c - 'A') <= 'Z' - 'A') || ((uint) (c - 'a') <= 'z' - 'a'); - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - static char ToLower (int c) - { - // check if the char is within the uppercase range - if ((uint) (c - 'A') <= 'Z' - 'A') - return (char) (c + 0x20); - - return (char) c; - } - - int Peek () - { - return text.Peek (); - } - - int Read () - { - int c; - - if ((c = text.Read ()) == -1) - return -1; - - if (c == '\n') { - LinePosition = 1; - LineNumber++; - } else { - LinePosition++; - } - - return c; - } - - // Note: value must be lowercase - bool NameIs (string value) - { - if (name.Length != value.Length) - return false; - - for (int i = 0; i < name.Length; i++) { - if (ToLower (name[i]) != value[i]) - return false; - } - - return true; - } - - void EmitTagAttribute () - { - attribute = CreateAttribute (name.ToString ()); - tag.Attributes.Add (attribute); - name.Length = 0; - } - - HtmlToken EmitCommentToken (string comment, bool bogus = false) - { - var token = CreateCommentToken (comment, bogus); - token.IsBangComment = bang; - data.Length = 0; - name.Length = 0; - bang = false; - return token; - } - - HtmlToken EmitCommentToken (CharBuffer comment, bool bogus = false) - { - return EmitCommentToken (comment.ToString (), bogus); - } - - HtmlToken EmitDocType () - { - var token = doctype; - data.Length = 0; - doctype = null; - return token; - } - - HtmlToken EmitDataToken (bool encodeEntities, bool truncated) - { - if (data.Length == 0) - return null; - - if (truncated && IgnoreTruncatedTags) { - data.Length = 0; - return null; - } - - var token = CreateDataToken (data.ToString ()); - token.EncodeEntities = encodeEntities; - data.Length = 0; - - return token; - } - - HtmlToken EmitCDataToken () - { - if (data.Length == 0) - return null; - - var token = CreateCDataToken (data.ToString ()); - data.Length = 0; - - return token; - } - - HtmlToken EmitScriptDataToken () - { - if (data.Length == 0) - return null; - - var token = CreateScriptDataToken (data.ToString ()); - data.Length = 0; - - return token; - } - - HtmlToken EmitTagToken () - { - if (!tag.IsEndTag && !tag.IsEmptyElement) { - switch (tag.Id) { - case HtmlTagId.Style: case HtmlTagId.Xmp: case HtmlTagId.IFrame: case HtmlTagId.NoEmbed: case HtmlTagId.NoFrames: - TokenizerState = HtmlTokenizerState.RawText; - activeTagName = tag.Name.ToLowerInvariant (); - break; - case HtmlTagId.Title: case HtmlTagId.TextArea: - TokenizerState = HtmlTokenizerState.RcData; - activeTagName = tag.Name.ToLowerInvariant (); - break; - case HtmlTagId.PlainText: - TokenizerState = HtmlTokenizerState.PlainText; - break; - case HtmlTagId.Script: - TokenizerState = HtmlTokenizerState.ScriptData; - break; - case HtmlTagId.NoScript: - // TODO: only switch into the RawText state if scripting is enabled - TokenizerState = HtmlTokenizerState.RawText; - activeTagName = tag.Name.ToLowerInvariant (); - break; - case HtmlTagId.Html: - TokenizerState = HtmlTokenizerState.Data; - - for (int i = tag.Attributes.Count; i > 0; i--) { - var attr = tag.Attributes[i - 1]; - - if (attr.Id == HtmlAttributeId.XmlNS && attr.Value != null) { - HtmlNamespace = attr.Value.ToHtmlNamespace (); - break; - } - } - break; - default: - TokenizerState = HtmlTokenizerState.Data; - break; - } - } else { - TokenizerState = HtmlTokenizerState.Data; - } - - var token = tag; - data.Length = 0; - tag = null; - - return token; - } - - // 8.2.4.69 Tokenizing character references - HtmlToken ReadCharacterReference (HtmlTokenizerState next) - { - int nc = Peek (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - data.Append ('&'); - - return EmitDataToken (true, false); - } - - c = (char) nc; - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': case '<': case '&': - // no character is consumed, emit '&' - TokenizerState = next; - data.Append ('&'); - return null; - } - - entity.Push ('&'); - - while (entity.Push (c)) { - Read (); - - if (c == ';') - break; - - if ((nc = Peek ()) == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - data.Append (entity.GetPushedInput ()); - entity.Reset (); - - return EmitDataToken (true, false); - } - - c = (char) nc; - } - - TokenizerState = next; - - data.Append (entity.GetValue ()); - entity.Reset (); - - return null; - } - - HtmlToken ReadGenericRawTextLessThan (HtmlTokenizerState rawText, HtmlTokenizerState rawTextEndTagOpen) - { - int nc = Peek (); - - data.Append ('<'); - - switch ((char) nc) { - case '/': - TokenizerState = rawTextEndTagOpen; - data.Append ('/'); - name.Length = 0; - Read (); - break; - default: - TokenizerState = rawText; - break; - } - - return null; - } - - HtmlToken ReadGenericRawTextEndTagOpen (bool decoded, HtmlTokenizerState rawText, HtmlTokenizerState rawTextEndTagName) - { - int nc = Peek (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (decoded, true); - } - - c = (char) nc; - - if (IsAsciiLetter (c)) { - TokenizerState = rawTextEndTagName; - name.Append (c); - data.Append (c); - Read (); - } else { - TokenizerState = rawText; - } - - return null; - } - - HtmlToken ReadGenericRawTextEndTagName (bool decoded, HtmlTokenizerState rawText) - { - var current = TokenizerState; - - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - - return EmitDataToken (decoded, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - if (NameIs (activeTagName)) { - TokenizerState = HtmlTokenizerState.BeforeAttributeName; - break; - } - - goto default; - case '/': - if (NameIs (activeTagName)) { - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - break; - } - goto default; - case '>': - if (NameIs (activeTagName)) { - var token = CreateTagToken (name.ToString (), true); - TokenizerState = HtmlTokenizerState.Data; - data.Length = 0; - name.Length = 0; - return token; - } - goto default; - default: - if (!IsAsciiLetter (c)) { - TokenizerState = rawText; - return null; - } - - name.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == current); - - tag = CreateTagToken (name.ToString (), true); - name.Length = 0; - - return null; - } - - // 8.2.4.1 Data state - HtmlToken ReadData () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - break; - } - - c = (char) nc; - - switch (c) { - case '&': - if (DecodeCharacterReferences) { - TokenizerState = HtmlTokenizerState.CharacterReferenceInData; - return null; - } - - goto default; - case '<': - TokenizerState = HtmlTokenizerState.TagOpen; - break; - //case 0: // parse error, but emit it anyway - default: - data.Append (c); - break; - } - } while (TokenizerState == HtmlTokenizerState.Data); - - return EmitDataToken (DecodeCharacterReferences, false); - } - - // 8.2.4.2 Character reference in data state - HtmlToken ReadCharacterReferenceInData () - { - return ReadCharacterReference (HtmlTokenizerState.Data); - } - - // 8.2.4.3 RCDATA state - HtmlToken ReadRcData () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - break; - } - - c = (char) nc; - - switch (c) { - case '&': - if (DecodeCharacterReferences) { - TokenizerState = HtmlTokenizerState.CharacterReferenceInRcData; - return null; - } - - goto default; - case '<': - TokenizerState = HtmlTokenizerState.RcDataLessThan; - return EmitDataToken (DecodeCharacterReferences, false); - default: - data.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.RcData); - - return EmitDataToken (DecodeCharacterReferences, false); - } - - // 8.2.4.4 Character reference in RCDATA state - HtmlToken ReadCharacterReferenceInRcData () - { - return ReadCharacterReference (HtmlTokenizerState.RcData); - } - - // 8.2.4.5 RAWTEXT state - HtmlToken ReadRawText () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - break; - } - - c = (char) nc; - - switch (c) { - case '<': - TokenizerState = HtmlTokenizerState.RawTextLessThan; - return EmitDataToken (false, false); - default: - data.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.RawText); - - return EmitDataToken (false, false); - } - - // 8.2.4.6 Script data state - HtmlToken ReadScriptData () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - break; - } - - c = (char) nc; - - switch (c) { - case '<': - TokenizerState = HtmlTokenizerState.ScriptDataLessThan; - break; - default: - data.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptData); - - return EmitScriptDataToken (); - } - - // 8.2.4.7 PLAINTEXT state - HtmlToken ReadPlainText () - { - int nc = Read (); - - while (nc != -1) { - char c = (char) nc; - - data.Append (c == '\0' ? '\uFFFD' : c); - nc = Read (); - } - - TokenizerState = HtmlTokenizerState.EndOfFile; - - return EmitDataToken (false, false); - } - - // 8.2.4.8 Tag open state - HtmlToken ReadTagOpen () - { - int nc = Read (); - char c; - - if (nc == -1) { - var token = IgnoreTruncatedTags ? null : CreateDataToken ("<"); - TokenizerState = HtmlTokenizerState.EndOfFile; - return token; - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append ('<'); - data.Append (c); - - switch ((c = (char) nc)) { - case '!': - TokenizerState = HtmlTokenizerState.MarkupDeclarationOpen; - break; - case '?': - TokenizerState = HtmlTokenizerState.BogusComment; - data.Length = 1; - data[0] = c; - break; - case '/': - TokenizerState = HtmlTokenizerState.EndTagOpen; - break; - default: - if (IsAsciiLetter (c)) { - TokenizerState = HtmlTokenizerState.TagName; - isEndTag = false; - name.Append (c); - } else { - TokenizerState = HtmlTokenizerState.Data; - } - break; - } - - return null; - } - - // 8.2.4.9 End tag open state - HtmlToken ReadEndTagOpen () - { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '>': // parse error - TokenizerState = HtmlTokenizerState.Data; - data.Length = 0; // FIXME: this is probably wrong - break; - default: - if (IsAsciiLetter (c)) { - TokenizerState = HtmlTokenizerState.TagName; - isEndTag = true; - name.Append (c); - } else { - TokenizerState = HtmlTokenizerState.BogusComment; - data.Length = 1; - data[0] = c; - } - break; - } - - return null; - } - - // 8.2.4.10 Tag name state - HtmlToken ReadTagName () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - - return EmitDataToken (false, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - TokenizerState = HtmlTokenizerState.BeforeAttributeName; - break; - case '/': - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - break; - case '>': - tag = CreateTagToken (name.ToString (), isEndTag); - data.Length = 0; - name.Length = 0; - - return EmitTagToken (); - default: - name.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.TagName); - - tag = CreateTagToken (name.ToString (), isEndTag); - name.Length = 0; - - return null; - } - - // 8.2.4.11 RCDATA less-than sign state - HtmlToken ReadRcDataLessThan () - { - return ReadGenericRawTextLessThan (HtmlTokenizerState.RcData, HtmlTokenizerState.RcDataEndTagOpen); - } - - // 8.2.4.12 RCDATA end tag open state - HtmlToken ReadRcDataEndTagOpen () - { - return ReadGenericRawTextEndTagOpen (DecodeCharacterReferences, HtmlTokenizerState.RcData, HtmlTokenizerState.RcDataEndTagName); - } - - // 8.2.4.13 RCDATA end tag name state - HtmlToken ReadRcDataEndTagName () - { - return ReadGenericRawTextEndTagName (DecodeCharacterReferences, HtmlTokenizerState.RcData); - } - - // 8.2.4.14 RAWTEXT less-than sign state - HtmlToken ReadRawTextLessThan () - { - return ReadGenericRawTextLessThan (HtmlTokenizerState.RawText, HtmlTokenizerState.RawTextEndTagOpen); - } - - // 8.2.4.15 RAWTEXT end tag open state - HtmlToken ReadRawTextEndTagOpen () - { - return ReadGenericRawTextEndTagOpen (false, HtmlTokenizerState.RawText, HtmlTokenizerState.RawTextEndTagName); - } - - // 8.2.4.16 RAWTEXT end tag name state - HtmlToken ReadRawTextEndTagName () - { - return ReadGenericRawTextEndTagName (false, HtmlTokenizerState.RawText); - } - - // 8.2.4.17 Script data less-than sign state - HtmlToken ReadScriptDataLessThan () - { - int nc = Peek (); - - data.Append ('<'); - - switch ((char) nc) { - case '/': - TokenizerState = HtmlTokenizerState.ScriptDataEndTagOpen; - data.Append ('/'); - name.Length = 0; - Read (); - break; - case '!': - TokenizerState = HtmlTokenizerState.ScriptDataEscapeStart; - data.Append ('!'); - Read (); - break; - default: - TokenizerState = HtmlTokenizerState.ScriptData; - break; - } - - return null; - } - - // 8.2.4.18 Script data end tag open state - HtmlToken ReadScriptDataEndTagOpen () - { - int nc = Peek (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitScriptDataToken (); - } - - c = (char) nc; - - if (c == 'S' || c == 's') { - TokenizerState = HtmlTokenizerState.ScriptDataEndTagName; - name.Append ('s'); - data.Append (c); - Read (); - } else { - TokenizerState = HtmlTokenizerState.ScriptData; - } - - return null; - } - - // 8.2.4.19 Script data end tag name state - HtmlToken ReadScriptDataEndTagName () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - - return EmitScriptDataToken (); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - if (NameIs ("script")) { - TokenizerState = HtmlTokenizerState.BeforeAttributeName; - break; - } - goto default; - case '/': - if (NameIs ("script")) { - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - break; - } - goto default; - case '>': - if (NameIs ("script")) { - var token = CreateTagToken (name.ToString (), true); - TokenizerState = HtmlTokenizerState.Data; - data.Length = 0; - name.Length = 0; - return token; - } - goto default; - default: - if (!IsAsciiLetter (c)) { - TokenizerState = HtmlTokenizerState.ScriptData; - name.Length = 0; - return null; - } - - name.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptDataEndTagName); - - tag = CreateTagToken (name.ToString (), true); - name.Length = 0; - - return null; - } - - // 8.2.4.20 Script data escape start state - HtmlToken ReadScriptDataEscapeStart () - { - int nc = Peek (); - - if (nc == '-') { - TokenizerState = HtmlTokenizerState.ScriptDataEscapeStartDash; - data.Append ('-'); - Read (); - } else { - TokenizerState = HtmlTokenizerState.ScriptData; - } - - return null; - } - - // 8.2.4.21 Script data escape start dash state - HtmlToken ReadScriptDataEscapeStartDash () - { - int nc = Peek (); - - if (nc == '-') { - TokenizerState = HtmlTokenizerState.ScriptDataEscapedDashDash; - data.Append ('-'); - Read (); - } else { - TokenizerState = HtmlTokenizerState.ScriptData; - } - - return null; - } - - // 8.2.4.22 Script data escaped state - HtmlToken ReadScriptDataEscaped () - { - HtmlToken token = null; - - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitScriptDataToken (); - } - - c = (char) nc; - - switch (c) { - case '-': - TokenizerState = HtmlTokenizerState.ScriptDataEscapedDash; - data.Append ('-'); - break; - case '<': - TokenizerState = HtmlTokenizerState.ScriptDataEscapedLessThan; - token = EmitScriptDataToken (); - data.Append ('<'); - break; - default: - data.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptDataEscaped); - - return token; - } - - // 8.2.4.23 Script data escaped dash state - HtmlToken ReadScriptDataEscapedDash () - { - HtmlToken token = null; - int nc = Peek (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitScriptDataToken (); - } - - switch ((c = (char) nc)) { - case '-': - TokenizerState = HtmlTokenizerState.ScriptDataEscapedDashDash; - data.Append ('-'); - Read (); - break; - case '<': - TokenizerState = HtmlTokenizerState.ScriptDataEscapedLessThan; - token = EmitScriptDataToken (); - data.Append ('<'); - Read (); - break; - default: - TokenizerState = HtmlTokenizerState.ScriptDataEscaped; - data.Append (c == '\0' ? '\uFFFD' : c); - break; - } - - return token; - } - - // 8.2.4.24 Script data escaped dash dash state - HtmlToken ReadScriptDataEscapedDashDash () - { - HtmlToken token = null; - - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitScriptDataToken (); - } - - c = (char) nc; - - switch (c) { - case '-': - data.Append ('-'); - break; - case '<': - TokenizerState = HtmlTokenizerState.ScriptDataEscapedLessThan; - token = EmitScriptDataToken (); - data.Append ('<'); - break; - case '>': - TokenizerState = HtmlTokenizerState.ScriptData; - data.Append ('>'); - break; - default: - TokenizerState = HtmlTokenizerState.ScriptDataEscaped; - data.Append (c); - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptDataEscapedDashDash); - - return token; - } - - // 8.2.4.25 Script data escaped less-than sign state - HtmlToken ReadScriptDataEscapedLessThan () - { - int nc = Peek (); - char c = (char) nc; - - if (c == '/') { - TokenizerState = HtmlTokenizerState.ScriptDataEscapedEndTagOpen; - data.Append (c); - name.Length = 0; - Read (); - } else if (IsAsciiLetter (c)) { - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscapeStart; - data.Append (c); - name.Append (c); - Read (); - } else { - TokenizerState = HtmlTokenizerState.ScriptDataEscaped; - } - - return null; - } - - // 8.2.4.26 Script data escaped end tag open state - HtmlToken ReadScriptDataEscapedEndTagOpen () - { - int nc = Peek (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitScriptDataToken (); - } - - c = (char) nc; - - if (IsAsciiLetter (c)) { - TokenizerState = HtmlTokenizerState.ScriptDataEscapedEndTagName; - data.Append (c); - name.Append (c); - Read (); - } else { - TokenizerState = HtmlTokenizerState.ScriptDataEscaped; - } - - return null; - } - - // 8.2.4.27 Script data escaped end tag name state - HtmlToken ReadScriptDataEscapedEndTagName () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - - return EmitScriptDataToken (); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - if (NameIs ("script")) { - TokenizerState = HtmlTokenizerState.BeforeAttributeName; - break; - } - - goto default; - case '/': - if (NameIs ("script")) { - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - break; - } - goto default; - case '>': - if (NameIs ("script")) { - var token = CreateTagToken (name.ToString (), true); - TokenizerState = HtmlTokenizerState.Data; - data.Length = 0; - name.Length = 0; - return token; - } - goto default; - default: - if (!IsAsciiLetter (c)) { - TokenizerState = HtmlTokenizerState.ScriptData; - return null; - } - - name.Append (c); - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptDataEscapedEndTagName); - - tag = CreateTagToken (name.ToString (), true); - name.Length = 0; - - return null; - } - - // 8.2.4.28 Script data double escape start state - HtmlToken ReadScriptDataDoubleEscapeStart () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - - return EmitScriptDataToken (); - } - - c = (char) nc; - - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': case '/': case '>': - if (NameIs ("script")) - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscaped; - else - TokenizerState = HtmlTokenizerState.ScriptDataEscaped; - name.Length = 0; - break; - default: - if (!IsAsciiLetter (c)) - TokenizerState = HtmlTokenizerState.ScriptDataEscaped; - else - name.Append (c); - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptDataDoubleEscapeStart); - - return null; - } - - // 8.2.4.29 Script data double escaped state - HtmlToken ReadScriptDataDoubleEscaped () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitScriptDataToken (); - } - - c = (char) nc; - - switch (c) { - case '-': - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscapedDash; - data.Append ('-'); - break; - case '<': - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscapedLessThan; - data.Append ('<'); - break; - default: - data.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptDataEscaped); - - return null; - } - - // 8.2.4.30 Script data double escaped dash state - HtmlToken ReadScriptDataDoubleEscapedDash () - { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitScriptDataToken (); - } - - switch ((c = (char) nc)) { - case '-': - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscapedDashDash; - data.Append ('-'); - break; - case '<': - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscapedLessThan; - data.Append ('<'); - break; - default: - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscaped; - data.Append (c == '\0' ? '\uFFFD' : c); - break; - } - - return null; - } - - // 8.2.4.31 Script data double escaped dash dash state - HtmlToken ReadScriptDataDoubleEscapedDashDash () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitScriptDataToken (); - } - - c = (char) nc; - - switch (c) { - case '-': - data.Append ('-'); - break; - case '<': - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscapedLessThan; - data.Append ('<'); - break; - case '>': - TokenizerState = HtmlTokenizerState.ScriptData; - data.Append ('>'); - break; - default: - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscaped; - data.Append (c); - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptDataEscapedDashDash); - - return null; - } - - // 8.2.4.32 Script data double escaped less-than sign state - HtmlToken ReadScriptDataDoubleEscapedLessThan () - { - int nc = Peek (); - - if (nc == '/') { - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscapeEnd; - data.Append ('/'); - Read (); - } else { - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscaped; - } - - return null; - } - - // 8.2.4.33 Script data double escape end state - HtmlToken ReadScriptDataDoubleEscapeEnd () - { - do { - int nc = Peek (); - char c = (char) nc; - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': case '/': case '>': - if (NameIs ("script")) - TokenizerState = HtmlTokenizerState.ScriptDataEscaped; - else - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscaped; - data.Append (c); - Read (); - break; - default: - if (!IsAsciiLetter (c)) { - TokenizerState = HtmlTokenizerState.ScriptDataDoubleEscaped; - } else { - name.Append (c); - data.Append (c); - Read (); - } - break; - } - } while (TokenizerState == HtmlTokenizerState.ScriptDataDoubleEscapeEnd); - - return null; - } - - // 8.2.4.34 Before attribute name state - HtmlToken ReadBeforeAttributeName () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - tag = null; - - return EmitDataToken (false, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - break; - case '/': - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - return null; - case '>': - return EmitTagToken (); - case '"': case '\'': case '<': case '=': - // parse error - goto default; - default: - TokenizerState = HtmlTokenizerState.AttributeName; - name.Append (c == '\0' ? '\uFFFD' : c); - return null; - } - } while (true); - } - - // 8.2.4.35 Attribute name state - HtmlToken ReadAttributeName () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - tag = null; - - return EmitDataToken (false, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - TokenizerState = HtmlTokenizerState.AfterAttributeName; - break; - case '/': - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - break; - case '=': - TokenizerState = HtmlTokenizerState.BeforeAttributeValue; - break; - case '>': - EmitTagAttribute (); - - return EmitTagToken (); - default: - name.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.AttributeName); - - EmitTagAttribute (); - - return null; - } - - // 8.2.4.36 After attribute name state - HtmlToken ReadAfterAttributeName () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - tag = null; - - return EmitDataToken (false, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - break; - case '/': - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - return null; - case '=': - TokenizerState = HtmlTokenizerState.BeforeAttributeValue; - return null; - case '>': - return EmitTagToken (); - case '"': case '\'': case '<': - // parse error - goto default; - default: - TokenizerState = HtmlTokenizerState.AttributeName; - name.Append (c == '\0' ? '\uFFFD' : c); - return null; - } - } while (true); - } - - // 8.2.4.37 Before attribute value state - HtmlToken ReadBeforeAttributeValue () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - tag = null; - - return EmitDataToken (false, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - break; - case '"': case '\'': - TokenizerState = HtmlTokenizerState.AttributeValueQuoted; - quote = c; - return null; - case '&': - TokenizerState = HtmlTokenizerState.CharacterReferenceInAttributeValue; - return null; - case '/': - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - return null; - case '>': - return EmitTagToken (); - case '<': case '=': case '`': - // parse error - goto default; - default: - TokenizerState = HtmlTokenizerState.AttributeValueUnquoted; - name.Append (c == '\0' ? '\uFFFD' : c); - return null; - } - } while (true); - } - - // 8.2.4.38 Attribute value (double-quoted) state - HtmlToken ReadAttributeValueQuoted () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - - return EmitDataToken (false, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '&': - TokenizerState = HtmlTokenizerState.CharacterReferenceInAttributeValue; - return null; - default: - if (c == quote) { - TokenizerState = HtmlTokenizerState.AfterAttributeValueQuoted; - quote = '\0'; - break; - } - - name.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.AttributeValueQuoted); - - attribute.Value = name.ToString (); - name.Length = 0; - - return null; - } - - // 8.2.4.40 Attribute value (unquoted) state - HtmlToken ReadAttributeValueUnquoted () - { - do { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - - return EmitDataToken (false, true); - } - - c = (char) nc; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - TokenizerState = HtmlTokenizerState.BeforeAttributeName; - break; - case '&': - TokenizerState = HtmlTokenizerState.CharacterReferenceInAttributeValue; - return null; - case '>': - attribute.Value = name.ToString (); - name.Length = 0; - - return EmitTagToken (); - case '\'': case '<': case '=': case '`': - // parse error - goto default; - default: - name.Append (c == '\0' ? '\uFFFD' : c); - break; - } - } while (TokenizerState == HtmlTokenizerState.AttributeValueUnquoted); - - attribute.Value = name.ToString (); - name.Length = 0; - - return null; - } - - // 8.2.4.41 Character reference in attribute value state - HtmlToken ReadCharacterReferenceInAttributeValue () - { - char additionalAllowedCharacter = quote == '\0' ? '>' : quote; - int nc = Peek (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - name.Length = 0; - - return EmitDataToken (false, true); - } - - c = (char) nc; - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': case '<': case '&': - // no character is consumed, emit '&' - name.Append ('&'); - break; - default: - if (c == additionalAllowedCharacter) { - // this is not a character reference, nothing is consumed - name.Append ('&'); - break; - } - - entity.Push ('&'); - - while (entity.Push (c)) { - Read (); - - if (c == ';') - break; - - if ((nc = Peek ()) == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - data.Length--; - data.Append (entity.GetPushedInput ()); - entity.Reset (); - - return EmitDataToken (false, true); - } - - c = (char) nc; - } - - var pushed = entity.GetPushedInput (); - string value; - - if (c == '=' || IsAlphaNumeric (c)) - value = pushed; - else - value = entity.GetValue (); - - data.Length--; - data.Append (pushed); - name.Append (value); - entity.Reset (); - break; - } - - if (quote == '\0') - TokenizerState = HtmlTokenizerState.AttributeValueUnquoted; - else - TokenizerState = HtmlTokenizerState.AttributeValueQuoted; - - return null; - } - - // 8.2.4.42 After attribute value (quoted) state - HtmlToken ReadAfterAttributeValueQuoted () - { - HtmlToken token = null; - int nc = Peek (); - bool consume; - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false, true); - } - - c = (char) nc; - - switch (c) { - case '\t': case '\r': case '\n': case '\f': case ' ': - TokenizerState = HtmlTokenizerState.BeforeAttributeName; - data.Append (c); - consume = true; - break; - case '/': - TokenizerState = HtmlTokenizerState.SelfClosingStartTag; - data.Append (c); - consume = true; - break; - case '>': - token = EmitTagToken (); - consume = true; - break; - default: - TokenizerState = HtmlTokenizerState.BeforeAttributeName; - consume = false; - break; - } - - if (consume) - Read (); - - return token; - } - - // 8.2.4.43 Self-closing start tag state - HtmlToken ReadSelfClosingStartTag () - { - int nc = Read (); - char c; - - if (nc == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false, true); - } - - c = (char) nc; - - if (c == '>') { - tag.IsEmptyElement = true; - - return EmitTagToken (); - } - - // parse error - TokenizerState = HtmlTokenizerState.BeforeAttributeName; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - - return null; - } - - // 8.2.4.44 Bogus comment state - HtmlToken ReadBogusComment () - { - int nc; - char c; - - do { - if ((nc = Read ()) == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - break; - } - - if ((c = (char) nc) == '>') - break; - - data.Append (c == '\0' ? '\uFFFD' : c); - } while (true); - - TokenizerState = HtmlTokenizerState.Data; - - return EmitCommentToken (data, true); - } - - // 8.2.4.45 Markup declaration open state - HtmlToken ReadMarkupDeclarationOpen () - { - int count = 0, nc; - char c = '\0'; - - while (count < 2) { - if ((nc = Peek ()) == -1) { - TokenizerState = HtmlTokenizerState.EndOfFile; - return EmitDataToken (false, true); - } - - if ((c = (char) nc) != '-') - break; - - // Note: we save the data in case we hit a parse error and have to emit a data token - data.Append (c); - Read (); - count++; - } - - if (count == 2) { - // "